DojoCalendar LIFF Development
Expert assistant for developing and maintaining the DojoCalendar Google Apps Script web application that serves dojo schedules through LINE LIFF integration.
Project Overview
This is a Google Apps Script (GAS) web app that:
Displays martial arts dojo schedules for LINE users via LIFFParses inline ICS calendar data per dojo groupDeploys with clasp and serves via HtmlService templatesRoutes based on LIFF state parameters to select group calendarsArchitecture
**Runtime:** Google Apps Script V8 (no Node.js APIs or bundlers)
**Entry Point:** `doGet` function in `src/main.js`
Merges LIFF state parametersSelects group from `g` query parameter or defaultParses ICS dataRenders `index.html` via `HtmlService.createTemplateFromFile`**Key Files:**
`src/main.js` - Entry point with doGet handler`src/api.js` - LIFF state merging, ICS parsing, event normalization`src/config.js` - Group definitions with inline ICS strings`src/index.html` - GAS HTML template with placeholders`src/appsscript.json` - Web app manifest (ANYONE_ANONYMOUS access)Instructions
1. Code Organization
Keep all globals and helper functions in `src/` directoryAvoid Node-only APIs (no `require`, `module.exports`, `process`, etc.)Use GAS V8 runtime features onlyMaintain separation: config in `config.js`, utilities in `api.js`, UI in `index.html`2. LIFF State Routing
LIFF adds state via `liff.state` query parameter (e.g., `?g=ro`).
The `mergeLiffStateParams_` function in `src/api.js`:
Extracts LIFF state from URLMerges it into `e.parameter` objectEnables group selection before renderingPreserve this routing logic when modifying parameter handling.
3. Group Configuration
Groups are defined in `src/config.js`:
```javascript
CONFIG.groups = {
groupKey: {
label: "Display Name",
ics: "BEGIN:VCALENDAR\n..." // inline ICS string
}
}
CONFIG.defaultGroup = "groupKey"
```
**Adding New Groups:**
1. Add entry to `CONFIG.groups` with unique key
2. Provide `label` for display
3. Include complete ICS block with:
- TZID: `Asia/Tokyo`
- Unique UID and DTSTAMP values per event
4. Optionally update `CONFIG.defaultGroup`
4. ICS Parsing
`parseIcsToEvents_` in `src/api.js` handles ICS parsing:
Unfolds folded lines (CRLF + space/tab continuation)Extracts: DTSTART, DTEND, SUMMARY, LOCATION, DESCRIPTIONUnescapes ICS text (\\n, \\,, etc.)Returns sorted array of event objects**Date Handling:**
Use `Utilities.formatDate(date, "Asia/Tokyo", format)` for all date operationsTimezone MUST be `Asia/Tokyo` to match ICS TZID5. Event Normalization
`normalizeEvent_` converts raw ICS events to UI format:
`start`: `yyyy/MM/dd HH:mm``end`: `HH:mm` (time only)`dateKey`: `yyyy/MM/dd` (for grouping)**CRITICAL:** Maintain these exact formats - the UI parses these strings for month grouping and weekday tagging.
6. HTML Template
`src/index.html` is a GAS HTML template using scriptlets:
`<?= eventsJson ?>` - JSON string of events`<?= groupLabel ?>` - Selected group display name**Template Rules:**
Keep all `<?= ... ?>` placeholders when editingRender DOM with plain JavaScript (no frameworks)Use `escapeHtml` function for ALL injected text contentTag weekends with class names for styling7. Styling
Minimal inline CSS approach:
`.sat` and `.sun` classes for weekend shading`.dow-tag` for weekday chipsAvoid external assets unless publicly hosted (GAS restrictions)8. Deployment with clasp
**Setup:**
```bash
npm install # Install clasp
npx clasp login # Authenticate (one-time)
```
**Commands:**
`npm run push` - Upload local code to GAS`npm run pull` - Download GAS code to local`npm run open` - Open script in GAS editor`npm run logs` - Stream Stackdriver logs**Requirements:**
Valid `.clasp.json` with scriptIdAuthenticated clasp session**Web App Deployment:**
Manifest in `src/appsscript.json`Access: `ANYONE_ANONYMOUS`Execute as: `USER_DEPLOYING`DO NOT change unless modifying auth model9. Testing
No automated tests currently.
**Manual Testing:**
1. Run `npm run push`
2. Visit deployed web app URL
3. Test with query parameters: `?g=i` or `?g=ro`
4. Verify ICS parsing and event rendering
5. Check weekend styling and date grouping
10. Future Development
Planned features (from README):
LIFF SDK initializationUser ID logging to Google SheetsAdd helpers in `api.js` for LIFF integrationWire LIFF fetch in `index.html` without breaking current schedule view**When adding LIFF features:**
Keep schedule view fully functionalAdd new functions to `api.js`Update template carefully to avoid breaking existing rendering11. Common Tasks
**Adding an Event Field:**
1. Update ICS parsing in `parseIcsToEvents_` to extract new field
2. Add normalization logic in `normalizeEvent_` if needed
3. Update template rendering in `index.html`
4. Ensure proper HTML escaping
**Changing Date Format:**
1. Update `normalizeEvent_` format strings
2. Update UI parsing logic in `index.html`
3. Test month grouping and weekday tags
4. Use `Utilities.formatDate` with `Asia/Tokyo`
**Debugging:**
1. Check Stackdriver logs: `npm run logs`
2. Add `Logger.log()` statements
3. Push and test in deployed environment
4. Use `?g=groupKey` to test specific groups
12. Constraints
**No bundlers** - GAS doesn't support webpack/rollup**No npm modules in runtime** - Only for dev tools (clasp)**Asia/Tokyo timezone** - Required for all date operations**String-based dates** - UI depends on specific format strings**Anonymous access** - Current deployment allows unauthenticated users**Inline ICS** - Calendar data embedded in config, not fetched13. Code Style
Use ES5-compatible syntax (GAS V8 limitations)Trailing underscore for private functions (`parseIcsToEvents_`)Global CONFIG object for configurationEscape all user-facing text with `escapeHtml`Comment complex ICS parsing logicWhen making changes, prioritize maintaining the existing schedule view functionality while preparing for future LIFF SDK integration.