Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
aff5ba5
docs: Add workbooks-list-url-params dev notes and implementation plan
KATO-Hiro Mar 20, 2026
c1dab00
Merge branch 'staging' of github.com:AtCoder-NoviSteps/AtCoderNoviSte…
KATO-Hiro Mar 21, 2026
eb229b2
docs: Update workbooks-list-url-params dev notes and add phase-11 plan
KATO-Hiro Mar 21, 2026
a21025a
feat(workbooks/types): Add WorkBookTab const object; derive order-pag…
KATO-Hiro Mar 21, 2026
4b71d36
feat(workbooks/utils): Add partitionWorkbooksAsMainAndReplenished uti…
KATO-Hiro Mar 21, 2026
cb94ce8
feat(workbooks/utils): Add URL param parsing and URL builder for work…
KATO-Hiro Mar 21, 2026
91fcd11
feat(workbooks/services): Add getPublishedWorkbooksByPlacement, getWo…
KATO-Hiro Mar 21, 2026
2453bea
feat(workbooks/server): Load workbooks from URL params; add CREATED_B…
KATO-Hiro Mar 21, 2026
a100fe6
feat(workbooks/components): Add SolutionWorkBookList with category Bu…
KATO-Hiro Mar 21, 2026
374d7f4
refactor(workbooks/components): CurriculumWorkBookList uses grade pro…
KATO-Hiro Mar 21, 2026
d9a20e4
refactor(workbooks/components): WorkbookTabItem removes store, expose…
KATO-Hiro Mar 21, 2026
e635c28
feat(workbooks): URL-driven tab/filter navigation; WorkBookList discr…
KATO-Hiro Mar 21, 2026
5b0667c
chore(workbooks): Remove stores replaced by URL params and local state
KATO-Hiro Mar 21, 2026
3066f27
test(e2e/workbooks): Update tests for URL param-driven filtering and …
KATO-Hiro Mar 21, 2026
1fa4184
docs: Mark phases 0-11 complete; add implementation lessons to plan
KATO-Hiro Mar 21, 2026
12fba53
docs: Update plan.md and fix e2e test formatting
KATO-Hiro Mar 21, 2026
1649cb6
chore: Untrack .vscode/settings.json and .claude/settings.local.json;…
KATO-Hiro Mar 21, 2026
dd61703
docs: Update phase-11 and plan.md for workbooks-list-url-params
KATO-Hiro Mar 21, 2026
dfb7921
refactor(workbooks): compute taskResultsWithWorkBookId once via \$der…
KATO-Hiro Mar 21, 2026
abecf45
fix(workbooks): address CodeRabbit findings from Phase 11 review
KATO-Hiro Mar 21, 2026
bea8a63
feat: Add CREATED_BY_USER tab auth guard and update dev notes
KATO-Hiro Mar 21, 2026
338a0da
docs: Update phase-11 dev notes
KATO-Hiro Mar 21, 2026
92f797c
docs: Add phase-12 plan for workbooks-list-url-params
KATO-Hiro Mar 21, 2026
36f44e6
feat: Phase 12 - admin unpublished view, URL state restore, empty state
KATO-Hiro Mar 21, 2026
74619a0
feat: Add empty state to Curriculum/Solution workbook lists and updat…
KATO-Hiro Mar 21, 2026
78b0ece
Merge branch 'staging' of github.com:AtCoder-NoviSteps/AtCoderNoviSte…
KATO-Hiro Mar 22, 2026
052cda5
docs: Add workbooks-list-url-params refactor notes
KATO-Hiro Mar 22, 2026
3511a3f
refactor: move PlacementQuery to workbook_placement.ts and add Soluti…
KATO-Hiro Mar 22, 2026
71dc787
refactor: add TSDoc to SolutionCategories type
KATO-Hiro Mar 22, 2026
c9526f3
refactor: workbook_url_params cleanup, JSDoc update, rename acc to pa…
KATO-Hiro Mar 22, 2026
566461b
refactor: reorder functions and test describes to match conceptual gr…
KATO-Hiro Mar 22, 2026
af251c7
test: add boundary grade (Q1, D1, D6) and category (DP, DATA_STRUCTUR…
KATO-Hiro Mar 22, 2026
c90e987
refactor: apply SolutionCategories plural type alias to components an…
KATO-Hiro Mar 22, 2026
ee2a339
refactor: rest spread WorkBookList props and replace handleTabChange …
KATO-Hiro Mar 22, 2026
fccf840
refactor: reorganize E2E test hierarchy for workbooks list
KATO-Hiro Mar 22, 2026
b068945
fix: use type-only import for SolutionCategories in SolutionWorkBookList
KATO-Hiro Mar 22, 2026
8033f62
docs: update refactor.md checklist to reflect completed phases
KATO-Hiro Mar 22, 2026
e29879c
docs: update coderabbit review status in refactor.md
KATO-Hiro Mar 22, 2026
b39ad17
docs: add plan lifecycle, language policy, and updated CodeRabbit tri…
KATO-Hiro Mar 22, 2026
109a966
fix: phase-N.md discarded after transferring lessons to plan.md (not …
KATO-Hiro Mar 22, 2026
ccde264
fix: CodeRabbit triage — write medium and above (including critical/h…
KATO-Hiro Mar 22, 2026
eadb45a
style: Add blank line before conditional in workbooks_list e2e test
KATO-Hiro Mar 22, 2026
bfda4d8
docs: Consolidate workbooks-list-url-params plan and remove phase files
KATO-Hiro Mar 22, 2026
4426de3
docs: Update svelte-components and testing rules, finalize workbooks …
KATO-Hiro Mar 22, 2026
46ef19e
docs: Merge refactor notes into plan and remove refactor.md
KATO-Hiro Mar 22, 2026
0181c5e
docs: Update coding-style and testing rules
KATO-Hiro Mar 22, 2026
58ffb2c
feat: add URL param persistence, admin visibility, and filter fixes f…
KATO-Hiro Mar 22, 2026
4e444cf
docs: Add Prisma relation filter INNER JOIN rule and finalize workboo…
KATO-Hiro Mar 22, 2026
9ce1f2a
refactor: Filter before each-block in CreatedByUserTable, add include…
KATO-Hiro Mar 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions .claude/rules/coding-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Before writing new logic, decide which layer it belongs to. Run this check at pl
- **`upsert`**: only use when the implementation performs both insert and update. For insert-only, use `initialize`, `seed`, or another accurate verb.
- **`any`**: before using `any`, check the value's origin — adding a missing `@types/*` or `devDependency` often provides the correct type.
- **UI labels**: if a label does not match actual behavior, update it or add an inline comment explaining the intentional mismatch.
- **Constant names**: reflect what the value IS (content), not what it is used for (purpose). e.g., a set holding all enum tab values is `EXISTING_TABS`, not `VALID_TABS`.
Comment thread
KATO-Hiro marked this conversation as resolved.
- **New files**: before naming a new file or directory, grep the relevant `src/` directory to confirm existing conventions. Confirm at plan time, not during implementation:
- Custom files in routes (utilities, helpers, etc.): `snake_case` (e.g., `user_profile.ts`)
- SvelteKit special files: follow framework conventions (`+page.svelte`, `+page.server.ts`, `+server.ts`)
Expand Down Expand Up @@ -70,10 +71,18 @@ Shared helper functions (used by two or more exports) should be grouped at the e

## Documentation

### Language Policy

Write all project documentation (plans, dev-notes, guides, refactor notes) in Japanese. Write all source code comments, TSDoc, commit messages, and test titles in English. This keeps documentation readable for the team while keeping code comments universally accessible and searchable.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
**Exception**: The `## CodeRabbit Findings` section in `refactor.md` must quote findings verbatim in their original language (English). Do not translate CodeRabbit output.

### TSDoc

Add TSDoc comments to every exported function, type, and class. The minimum required fields are `@param` (for non-obvious parameters) and `@returns` (when the return value is not evident from the type). One-liner `/** ... */` is sufficient for simple cases; use multi-line only when behavior needs explanation.

For optional parameters with a default, state it explicitly in `@param`: `Defaults to false.`

```typescript
/** Returns the URL slug for a workbook, falling back to the workbook ID. */
export function getUrlSlugFrom(workbook: WorkbookList): string { ... }
Expand Down Expand Up @@ -158,9 +167,11 @@ update payload, not the reactivity system.

### CodeRabbit Review: Severity Triage

When running `coderabbit review --plain` at a Phase milestone:
Run `coderabbit review --plain` once after all phases are complete (not on every commit).

**Triage by severity:**

- **critical / high**: fix before starting the next Phase
- **low / info**: review before the next Phase starts; fix immediately only if security- or regression-related; otherwise defer to final PR review (alongside CodeRabbit CI comments)
- **critical / high / potential_issue (medium)**: Write all findings verbatim to a `## CodeRabbit Findings` section in `refactor.md`. The user decides which to fix before opening the PR. Do not fix any of these findings unilaterally.
- **nitpick / info**: Defer to PR CI — CodeRabbit will re-comment on the open PR.

Run once per Phase boundary — not on every commit.
Writing medium-and-above findings to `refactor.md` serves a dual purpose: it gives the user full visibility for a fix/defer decision, and it builds the implementer's understanding of recurring quality issues.
9 changes: 9 additions & 0 deletions .claude/rules/prisma-db.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ Prefer `createMany({ skipDuplicates: true })` over catching P2002 for expected u

`z.number().positive()` passes decimals. For Prisma `Int` fields use `z.number().int().positive()`.

## Relation Filter Exclusion

Filtering on a relation field (e.g. `where: { placement: { type: 'CURRICULUM' } }`)
performs an INNER JOIN internally — rows without a matching relation record are
automatically excluded. This is not an IS NOT NULL check; the mechanism is the JOIN.

When documenting this behavior, write "excluded by INNER JOIN" rather than
"implicitly includes IS NOT NULL".

## Validate Constraints

Prisma does not support `@@check`. To add one:
Expand Down
44 changes: 39 additions & 5 deletions .claude/rules/svelte-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,27 @@ Use `$props()`, `$state()`, `$derived()`, `$effect()` in all components. Props p

## Flowbite Svelte

Import from `flowbite-svelte`. Use Tailwind CSS v4 utility classes. Dark mode: `dark:` prefix.
Import from `flowbite-svelte`. Use Tailwind CSS v4 utility classes. Dark mode: `dark:` prefix. Important modifier: `dark:text-xxx!` (v4 syntax) — the v3 form `dark:!text-xxx` is invalid.

### ButtonGroup: No Responsive Wrapping

`ButtonGroup` uses `flex` internally — buttons do not wrap on narrow screens. When wrapping is needed, use `<div class="flex flex-wrap gap-1">` with individual `Button` components (reference: `TaskTable.svelte`).

When copying button styles from a reference component, always check all three axes: `color`, `size`, and `class`. Omitting `color` applies Flowbite's default (filled blue).

## `let`/`const` — Reactive Data Requires `$derived`

Plain `let` or `const` in Svelte 5 component `<script>` executes once at component creation. Values derived from props or server data must use `$derived()`:

```md
// Bad: captures only the initial value — won't update when data reloads
let user = data.loggedInUser; const categories = availableCategories.filter(...);

// Good
let user = $derived(data.loggedInUser); let categories = $derived(availableCategories.filter(...));
```

`pnpm check` warns: "This reference only captures the initial value."

## `$state()` Initialization with `$props()`

Expand Down Expand Up @@ -107,16 +127,30 @@ replaceState(buildUpdatedUrl($page.url, activeTab), {});

## `{#each}` — Keys and Empty-list Fallback

Always provide a key expression when the list or its items may change dynamically. This is especially critical when the block contains an inner `{#if}` — without a key, Svelte reuses DOM nodes by position, so filtering can silently bind data to the wrong element:
Always provide a key expression when the list or its items may change dynamically.

**Filter before `{#each}`, not inside it.** When visibility depends on a predicate (e.g. `canRead`), derive a filtered list once and iterate over it — never repeat the predicate inside a `{#if}` within the loop. This avoids computing the condition twice and keeps the template clean:

```svelte
{#each workbooks as workbook (workbook.id)}
{#if canRead(workbook)}
<Row {workbook} />
<!-- Bad: canRead computed twice — once for count, once in template -->
let visibleCount = $derived(items.filter((i) => canRead(i)).length);

{#each items as item (item.id)}
{#if canRead(item)}
<Row {item} />
{/if}
{/each}

<!-- Good: filter once, iterate over the result -->
let visibleItems = $derived(items.filter((i) => canRead(i)));

{#each visibleItems as item (item.id)}
<Row {item} />
{/each}
```

An inner `{#if}` inside `{#each}` is still valid for conditions unrelated to list membership (e.g. feature flags, role checks that don't affect count).

Use `{:else}` to render a placeholder when the list is empty — no wrapper conditional needed:

```svelte
Expand Down
48 changes: 46 additions & 2 deletions .claude/rules/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ E2E test files must use the `.spec.ts` extension. `playwright.config.ts` matches
## Assertions

- Use `toBe(true)` / `toBe(false)` over `toBeTruthy()` / `toBeFalsy()`
- For DB query tests, assert `orderBy`, `include`, and other significant parameters with `expect.objectContaining` — not just `where`
- For DB query tests, assert `orderBy`, `include`, and other significant parameters with `expect.objectContaining` — not just `where`. When a returned field (e.g. `authorName`) depends on an `include` relation, that `include` clause must be part of the assertion, or a regression in the query shape will go undetected
- Enum membership: `in` traverses the prototype chain; use `Object.hasOwn(Enum, value)` instead
- **E2E state transitions**: after an interaction that changes element state (active tab, toggle, selection), assert the _new_ state — not just that the element is visible, which may have been true before the interaction. Assert an active CSS class, `aria-selected`, or similar attribute instead of `toBeVisible()`

Expand Down Expand Up @@ -118,7 +118,51 @@ Stop the split if internal helpers (e.g. `fetchUnplacedWorkbooks`) would be frag

Use Nock for external HTTP calls. See `src/test/lib/clients/` for examples.

## Flowbite Toggle in E2E Tests
## Test Order Mirrors Source Order

Order `describe` blocks in service and utils test files to match the declaration order of functions in the source file. Misalignment makes it harder to cross-reference tests and implementation.

## E2E Tests

### No Path Aliases

The `e2e/` directory is outside SvelteKit's build pipeline — `$lib`, `$features`, and other path aliases are not resolved. Define URL string values as local constants with a reference comment:

```typescript
// Mirrors WorkBookTab.SOLUTION from $features/workbooks/types/workbook
const TAB_SOLUTION = 'solution';
```

Avoid importing types from `src/` in E2E test files.

### Describe Hierarchy

When a `describe` block for a user role grows large, split it by behavioral dimension rather than adding more flat `test()` calls:

```typescript
test.describe('logged-in user', () => {
test.describe('tab visibility', () => { ... });
test.describe('URL parameter handling', () => { ... });
test.describe('navigation interactions', () => { ... });
test.describe('session state', () => { ... });
});
```

### Parameterized Tests

Playwright has no native `test.each`. Use `for...of` loops — the official recommended pattern:

```typescript
// Mirrors TaskGrade from $lib/types/task — do not import from src/ in E2E files
const GRADES = ['Q10', 'Q9', 'Q8'] as const;

for (const grade of GRADES) {
await gradeButton(grade).click();
await expect(page).toHaveURL(`?grades=${grade}`);
}
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

### Flowbite Toggle

Flowbite's `Toggle` renders an `sr-only` `<input type="checkbox">` inside a `<label>`. Clicking the input directly fails because the visual `<span>` sibling intercepts pointer events. Click the label wrapper instead:

Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ vite.config.ts.timestamp-*

# VS Code
.history
.vscode/

# Local AI tool settings
.claude/settings.local.json

# Vercel
.vercel
Expand All @@ -153,3 +157,4 @@ prisma/.fabbrica

# Directory for playwright test results
test-results

5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ Always prefer simplicity over pathological correctness. YAGNI, KISS, DRY. No bac

**When implementing:**

1. Use `/writing-plans` to generate a phased plan (2–5-min tasks, lower risk → higher risk order). Verify each task before starting:
1. Use `/writing-plans` to generate a phased plan (2–5-min tasks, lower risk → higher risk order). Store the plan at `docs/dev-notes/YYYY-MM-DD/{task-name-en}/plan.md`. Split into `phase-N.md` files when the plan exceeds 200 lines or has 5+ phases. Each plan must include: overview, design rationale, rejected alternatives, and a per-phase summary. Write plans in Japanese; source code comments in English. Verify each task before starting:
- Which layer? (prisma / server / zod / types / fixtures / services / utils / stores / routes / components) — split if 2+ layers
- Single responsibility: one purpose per task
- Existing util/service/type? Search before creating
- Test name: state it in the task description
2. Before writing a new function, search `src/lib/utils/`, `src/lib/services/`, `src/features/*/utils/` and `src/features/*/services/` for existing implementations; extract shared logic there when it appears in 2+ places
3. Write tests first, then implement production code, then verify with `pnpm test:unit`
4. Review critically after implementing: flag YAGNI violations, over-abstraction, missing tests
5. Run `/session-close` at the end of each session: updates plan checklist, proposes rule/skill additions, checks for bloat, and detects repeated instructions
5. After all phases complete (feature and refactor branches only — not hotfixes or dependency bumps): run a mandatory refactor cycle. Produce `refactor.md` in the same directory as the plan, documenting: design decisions made, changes explicitly rejected and why, remaining tasks, and per-phase lessons. Transfer all lessons to `plan.md`, then discard `phase-N.md` files. Run `coderabbit review --plain`; write all findings of `critical` / `high` / `potential_issue` (medium) to a `## CodeRabbit Findings` section in `refactor.md`. The user decides which to fix before opening a PR; do not fix any finding unilaterally. `nitpick` findings defer to PR CI.
6. Run `/session-close` at the end of each session: updates plan checklist, proposes rule/skill additions, checks for bloat, and detects repeated instructions

## Tech Stack

Expand Down
Loading
Loading