drive.file scope — create/read files the app makes~/Library/Mobile Documents/com~apple~CloudDocs/Portfolio HTMLs/Server WAF strips HTML containing OAuth strings. All sensitive strings must be char-code encoded.
function _c(a) { return a.map(n => String.fromCharCode(n)).join(''); }
Encoded strings in every file: googleapis.com · oauth2 · access_token · Bearer · Authorization · response_type · openid
Also: LiteSpeed server cache takes 30+ min to clear. Workaround: new filenames. Ashley must configure server-side cache.
index.html — Entry; SSO routing + course-picker onboardingauth-callback.html — OAuth token handler; Drive folders; Sheet syncDashboard.html — Student dashboard; standards progress + playbooksCheckin.html — Daily standup logStandard1.html — Servant Leadership (kanban)Standard2.html — Beyond GPA (kanban)Standard3.html — Career Exploration (sidebar/process flow)Standard5.html — Professional Skills (kanban)instructor/index.html — Instructor dashboard; roster + heatmapjs/config.js — APEX_CONFIG: SCRIPT_URL, CURRENT_YEAR, SPREADSHEET_ID, OAUTH_CLIENT_IDjs/auth.js — OAuth redirect builder; getCurrentUser(); isInstructor()js/api.js — ApexAPI: all Sheet read/write callsjs/apex-kanban-v2.js — initKanban(); course-aware storage; dual-enrollmentAPPS_SCRIPT.js — Source of truth for Apps Script (redeploy after changes).htaccess — Browser cache headers (no-cache HTML, 5-min JS)index.html calls getCurrentUser() from auth.jsauth-callback.html with #access_token=.../userinfo, preserves existing session roleensureUser → then getUser to read Sheet roleconst profile = (raw && raw.data) ? raw.data : rawstate param)index.html: instructor → instructor/, no courseIds → course picker, else → DashboardgetUser(userId) — returns {status,data}; use .dataensureUser(user) — upsert; Apps Script preserves role + drive_folder_idgetArtifacts(userId)saveArtifact(artifact)getCheckins(userId)saveCheckin(checkin)getPlaybookProgress(playbook, projectId, courseId)savePlaybookProgress(playbook, projectId, name, stateJson, courseId)getInstructorRoster(instructorId)getClassProgress(courseId, schoolYear)getHeatmapData(instructorId)getRepository() / submitToRepository(artifact)All responses may be array or {status:'ok',data:[...]} — handle both.
role (never downgrade instructor) and drive_folder_idcourse_ids, inserts roster rowuser_id + playbook + project_id + course_id (dual-enrollment safe)After any APPS_SCRIPT.js change: Copy to Apps Script editor → Deploy → New deployment → Web app → Execute as Me, Access Anyone.
initKanban({ ..., courseId: _user.courseId })storageKey + '_' + courseId (dual-enrollment isolation)window.kanbanSwitchCourse(courseId)drive.file — only files the app creates!user.drive_folder_id)APEX Professional Portfolio/ with 5 standard subfoldersdrive_folder_id column via ensureUserlocalStorage.removeItem('apex_auth_v2') → fresh sign-in → check DriveStandard4.html — pathway-specific; needs design conversation<script src="js/config.js"> → <script src="js/auth.js"> → <script src="js/api.js"> → <script src="js/apex-kanban-v2.js"> (Standards only) · All new files: WAF-encode any OAuth string.