Skip to content

Refactor: consolidate page metadata and improve code organization - #56

Merged
injoon5 merged 7 commits into
mainfrom
claude/kind-hamilton-d7hk64
Sep 25, 2026
Merged

injoon5 merged 7 commits into
mainfrom
claude/kind-hamilton-d7hk64

Conversation

@injoon5

@injoon5 injoon5 commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Summary

This refactor consolidates page metadata handling into a reusable PageMeta component and reorganizes imports/utilities across the application for better maintainability and consistency.

Key Changes

New Components & Utilities

  • PageMeta.svelte: New component that centralizes Open Graph and meta tag generation for all pages, replacing inline metadata logic
  • RelativeTime.svelte: New component for displaying relative timestamps that resolve after mount (SSR-safe)
  • FileIcon.svelte: New component for rendering file type icons (images vs documents)
  • AdminNoticeRow.svelte: Extracted notice row rendering logic from admin page for reusability
  • NoticeEditor.svelte: Extracted notice form editor into a dedicated component with NoticeForm type and emptyNoticeForm() helper
  • lib/errors.ts: New utility for centralized error handling with adminErrorMessage() function
  • lib/dom.ts: New utility for DOM operations (blurActiveElement(), holdComposingEnter())
  • lib/timetable.ts: New utility for timetable week matching logic
  • packages/backend/convex/text.ts: New backend utility for text processing (URL basename extraction, description summarization)

Page Updates

All pages now use the new PageMeta component instead of inline metadata:

  • +page.svelte (home)
  • calendar/+page.svelte
  • timetable/+page.svelte
  • meals/+page.svelte
  • notices/+page.svelte
  • notice/[id]/+page.svelte
  • admin/+page.svelte

Code Organization Improvements

  • Import standardization: Consistent quote style and import ordering across all files
  • Calendar page: Refactored date handling to use derived state and simplified week calculation
  • Admin page: Extracted notice editor and row components; improved form state management
  • Timetable page: Separated week queries; improved tab state handling
  • Meals page: Simplified meal type selection and week data handling
  • FileUpload: Improved file upload state management and error handling

Backend Improvements

  • dates.ts: Added scheduleWindow() function for pagination bounds calculation
  • schema.ts: Added weekStart field to timetable documents for better week tracking
  • auth.ts: Improved error handling with ConvexError for unauthorized access
  • notices.ts: Enhanced error handling and validation

Utility Refactoring

  • lib/date.ts: Consolidated date utilities; re-exported backend functions for client use
  • lib/convex.ts: Added orFallback() helper for graceful fallback handling
  • lib/format.ts: Improved file size formatting and added parseInfoRows() utility
  • lib/transitions.ts: Simplified motion vocabulary documentation
  • lib/eventChrome.ts: Added CUSTOM_COLOR_LABEL constant

Configuration

  • svelte.config.js: Enhanced with environment loading for local Convex backend support
  • app.html: Cleaned up splash screen configuration

Notable Implementation Details

  • PageMeta component handles both website and article schema types with proper Open Graph tags
  • RelativeTime uses onMount to ensure SSR/hydration consistency
  • NoticeEditor extracted with proper form state management and file upload integration
  • Calendar refactored to use month indexing (year * 12 + month) for cleaner pagination logic
  • Timetable now maintains both week queries separately to avoid stale data issues
  • Error handling centralized with adminErrorMessage() for consistent user feedback

https://claude.ai/code/session_01C7d8MT7fdW13YS6oRm7q3p

Bugs
- Enter on 취소 in a delete confirmation no longer confirms the delete.
- Removing an attachment only detaches it; dropped files are deleted once
  the save lands, and unsaved uploads are deleted when the editor is
  abandoned. One failed upload no longer discards the others.
- Markdown keeps tables and fenced code intact, and leaves YouTube URLs
  that are already link targets alone.
- Timestamps format in the school's time zone, not the server's UTC.
- Notice summaries skip leading blank lines and strip markdown, so a
  description starting with a blank line still makes the card a link.
- The notice editor guards against double submit.
- Snapshotting a week into the standing timetable places slots by period.

Logic
- The schedule sync and calendar share one window keyed on the school
  year, so January and February can reach the months since March.
- Open tabs reload their data when the KST day, 4pm rollover, or dinner
  cutoff passes.
- Fetched timetable rows are stamped with their Monday and matched on it,
  so a stale row is never shown as this week; the timetable page labels
  the week it shows.
- Auth failures are ConvexErrors; an expired session returns the admin to
  the PIN screen, and notice validation errors reach the editor.

UI
- Calendar grid has a fixed width, so long event titles no longer widen
  cells month to month.
- Class label in notice titles comes from config; notice pages get og:url.
- Admins can edit an empty standing timetable directly.
- Calendar legend lists 학급 일정; colour picker labels are Korean.
- Meals hide the type toggle when only one type exists and show 원산지.
- ErrorState no longer prints raw errors; stale upload hint removed; file
  input resets so the same file can be picked again.
- app.html: absolute splash paths, one status-bar style, closing body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7d8MT7fdW13YS6oRm7q3p
Ran the app against a local Convex backend seeded with real school data and
drove every page with Playwright at phone and desktop widths.

Fixes
- Universal loads pass SvelteKit's fetch to the Convex client, so hydration
  reuses the SSR responses instead of re-querying every source.
- Drawer: Escape closes it wherever focus is, and focus returns to the
  control that opened it. The calendar's add field still cancels only
  the form on Escape.
- Timetable cell editor keeps its content while the sheet slides out.
- Markdown tables get styles; they rendered as run-together text.
- Four-syllable subjects (추석연휴) no longer truncate on phones.
- Copy button stays disabled when there is no 수행평가 to share.
- Meal drawer's nutrient grid is two columns on phones.
- CSP allows a local Convex backend's origin in development.
- File drop zone no longer flickers when dragging over its children.

Structure
- Shared PageMeta, SectionHeader, RelativeTime and FileIcon components
  replace per-page head boilerplate and repeated markup.
- Admin notice editor and row are components, shared by the current and
  past-month lists.
- Date parts and short dates come from one helper; notice summaries use
  the backend's summarizeDescription (moved to convex/text.ts).
- orFallback, blurActiveElement, holdComposingEnter and a `.field` input
  class replace repeated inline code.
- Calendar paging uses a single month index; cell styling is a function.

Comments across the frontend and backend are cut down to the why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7d8MT7fdW13YS6oRm7q3p
Measured frame by frame with scripted touch gestures on a phone viewport.

- Opening no longer flashes the sheet at rest for a frame before it
  slides up: it starts off screen and is measured after layout.
- Tap-to-open and dismiss are critically damped, as UIKit's are. Only a
  release that carried momentum overshoots (0.8 / 0.3, Apple's drawer).
- Dismiss aims past the edge and cuts off there, so the critically damped
  tail doesn't leave the sheet creeping at the bottom of the screen.
- Release velocity is the sheet's own over the last 100ms, not the
  finger's last event pair. A swipe the content scroller absorbed no
  longer bounces a sheet that never moved, and rubber-banded releases
  hand off the banded speed.
- Upward momentum now counts in the dismiss decision: flicking up rescues
  a sheet dragged most of the way down. The threshold is half the height
  of the projected resting point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7d8MT7fdW13YS6oRm7q3p
@vercel

vercel Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
class-info Ready Ready Preview Sep 25, 2026 1:12pm UTC

Applied when meals are stored and again when they are served, so rows
already in the database come out clean without a re-fetch. Only a y after
a non-Latin character is dropped, so English words ending in y survive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7d8MT7fdW13YS6oRm7q3p
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7d8MT7fdW13YS6oRm7q3p
Audited against the skill's conformance checklist and fixed the failures.

Installation and assets
- Add apple-mobile-web-app-capable. Without it iOS ignored every startup
  image; the unprefixed tag alone doesn't enable them.
- Generate startup images, their links and manifest icons from one device
  table and one mark (scripts/generate-app-assets.py). Adds the missing
  iPhone Air size and a light set: the old images were all dark, so light
  mode launched dark then flashed to the light page. Both sets now match
  the first rendered background. 22 sizes x 2 orientations x 2 appearances.
- Default apple-touch-icon is the 180px opaque PNG (it was 57px); drop the
  legacy sizes.
- Serve a web app manifest (name from SITE_NAME, standalone, 192/512 and
  maskable icons).
- theme-color values match the real backgrounds; drop obsolete metas.

Layout and overlays
- The top safe-area inset moves from body padding to the sticky header, so
  the header clears the status bar once scrolled in a Home Screen app.
- The sheet's last section clears the home indicator.
- A fixed edge strip, the header colour with the dim applied, makes Safari
  darken its status bar with the page; the sticky header otherwise keeps
  the bar undimmed under a sheet.
- Single-finger drags on the scrim no longer scroll the page behind it;
  pinch zoom still passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7d8MT7fdW13YS6oRm7q3p
@injoon5
injoon5 merged commit 18f5d7a into main Sep 25, 2026
3 checks passed

This branch was successfully deployed

1 active deployment
Preview — 72d7463a Deployed Sep 25, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants