Skip to content

✨ Enable to use url slug in workbook page (#2020) #2126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 45 commits into from
Jun 5, 2025
Merged

Conversation

KATO-Hiro
Copy link
Collaborator

@KATO-Hiro KATO-Hiro commented Jun 2, 2025

close #2020

Summary by CodeRabbit

  • New Features

    • Added support for optional, unique URL slugs for workbooks, enabling URL-friendly identification.
    • Administrators can set and edit URL slugs with validation on allowed characters and length.
    • Workbooks are accessible via either numeric ID or URL slug in URLs.
    • Introduced a reusable error message component for improved user feedback on permission errors.
  • Bug Fixes

    • Enhanced error handling and user feedback during workbook updates.
  • Documentation

    • Updated comments to indicate grading criteria are now publicly available.
  • Tests

    • Added extensive tests for URL slug validation, parsing, and schema integration.
  • Refactor

    • Centralized and streamlined workbook lookup logic supporting both IDs and slugs.
    • Reorganized import statements for clarity and maintainability.

Copy link

coderabbitai bot commented Jun 2, 2025

"""

Walkthrough

A new optional, unique URL slug field (urlSlug) has been added to the workbook entity across the database schema, backend services, and frontend components. Supporting validation, utilities, and tests were implemented to ensure slugs are correctly formatted, unique, and used in URLs. Related CRUD operations and UI were updated accordingly.

Changes

File(s) Change Summary
prisma/ERD.md, prisma/schema.prisma, prisma/migrations/.../migration.sql Added optional unique urlSlug field to workbook schema/model and database migration.
src/lib/types/workbook.ts, src/lib/zod/schema.ts Added optional urlSlug property to type and schema, with validation for allowed characters and length.
src/lib/utils/url.ts, src/test/lib/utils/url.test.ts Implemented isValidUrlSlug validator and comprehensive tests for valid/invalid slugs.
src/lib/utils/workbook.ts, src/test/lib/utils/workbook.test.ts Updated utilities to support slug parsing and lookup; added tests for slug parsing.
src/lib/services/workbooks.ts Added slug-based retrieval, uniqueness check, and included urlSlug in create operation.
src/lib/components/WorkBook/WorkBookForm.svelte, .../WorkBookInputFields.svelte Added slug input field (admin only), two-way binding, and updated import paths.
src/lib/components/WorkBooks/TitleTableBodyCell.svelte, .../WorkBookBaseTable.svelte Updated links to use slug if present, with helper function for URL generation.
src/routes/workbooks/[slug]/+page.server.ts, .../edit/[slug]/+page.server.ts Refactored to resolve workbook by slug or ID, using new utilities; improved error handling and normalization.
src/test/lib/zod/workbook_schema.test.ts Added/updated tests for schema validation of urlSlug, including edge cases and helper functions.
prisma/migrations/migration_lock.toml Minor comment punctuation and newline update (no functional impact).
prisma/schema.prisma Updated enum comment for TaskGrade (no functional impact).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant Server
    participant DB

    User->>UI: Enter or view workbook URL (with slug or ID)
    UI->>Server: Request workbook details (by slug or ID)
    Server->>DB: Lookup workbook by slug or ID
    DB-->>Server: Return workbook data
    Server-->>UI: Send workbook details
    UI-->>User: Display workbook details (URL uses slug if available)
Loading
sequenceDiagram
    participant Admin
    participant UI
    participant Server
    participant DB

    Admin->>UI: Fill in workbook form (with urlSlug)
    UI->>Server: Submit create workbook request
    Server->>DB: Check if urlSlug exists
    DB-->>Server: Return existence check
    alt Slug exists
        Server-->>UI: Return error (slug duplicate)
    else Slug does not exist
        Server->>DB: Create workbook with urlSlug
        DB-->>Server: Confirm creation
        Server-->>UI: Return success
    end
    UI-->>Admin: Show result
Loading

Assessment against linked issues

Objective Addressed Explanation
Add optional urlSlug to Prisma schema, types, zod/schema, with validation (a-z, 0-9, hyphen, 0-30 chars, regex, etc.) (#2020)
Update CRUD: include urlSlug in create, and fetch workbook by slug or ID (#2020)
Update UI: input for slug, display URL using slug if present, fallback to ID, responsive design (#2020)
Update server-side logic: edit/detail pages, use utils, permission checks, error handling (#2020)
Add/Update utils and tests: slug parsing/validation, zod/schema, edge cases, test coverage (#2020)

Assessment against linked issues: Out-of-scope changes

Code Change (file_path) Explanation
Update enum comment for TaskGrade in prisma/schema.prisma The change to the enum comment about grading criteria being public is unrelated to the slug feature.
Minor punctuation and newline update in prisma/migrations/migration_lock.toml This is a trivial documentation/comment change not related to the objectives.

Poem

A workbook hops with a brand new name,
Slugs unique—no two the same!
Admins pick slugs with care and pride,
URLs now sleek, no digits to hide.
Tests abound, and checks are tight,
Rabbits cheer: “Our links are right!”
🐇✨
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 958ed6c and 954d72d.

📒 Files selected for processing (2)
  • src/lib/utils/workbooks.ts (1 hunks)
  • src/routes/sitemap.xml/+server.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/routes/sitemap.xml/+server.ts
  • src/lib/utils/workbooks.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: preview
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (1)
src/routes/workbooks/edit/[slug]/+page.server.ts (1)

78-83: 🛠️ Refactor suggestion

Fix inconsistency with load function approach.

The actions function uses findWorkBookIdFrom followed by error handling, while the load function uses getWorkbookWithAuthor directly. This creates inconsistent error handling paths and redundant logic.

Apply this diff to align with the load function approach:

  const slug = params.slug.toLowerCase();
-  const workBookId = await findWorkBookIdFrom(slug);
-
-  if (workBookId === null) {
-    error(BAD_REQUEST, '不正な問題集idです。');
-  }
+  const workBookWithAuthor = await getWorkbookWithAuthor(slug);
+  const workBookId = workBookWithAuthor.workBook.id;

This ensures consistent error handling and avoids the redundant slug resolution call.

🧹 Nitpick comments (3)
src/lib/components/WorkBooks/TitleTableBodyCell.svelte (1)

14-18: LGTM! Consider extracting to shared utility function.

The fallback logic is correct and handles the case where urlSlug might be null or undefined. However, this exact function appears to be duplicated in other components.

Consider extracting this function to a shared utility module to avoid code duplication:

-  function getUrlSlugFrom(workbook: WorkbookList): string {
-    const slug = workbook.urlSlug;
-
-    return slug ? slug : workbook.id.toString();
-  }

Then import and use the shared function from $lib/utils/workbook.ts.

src/lib/services/workbooks.ts (1)

99-101: Simplify the boolean expression.

The ternary operator is unnecessary here as suggested by the static analysis tool.

Apply this diff to simplify the expression:

-async function isExistingUrlSlug(slug: string): Promise<boolean> {
-  return (await getWorkBookByUrlSlug(slug)) ? true : false;
-}
+async function isExistingUrlSlug(slug: string): Promise<boolean> {
+  return Boolean(await getWorkBookByUrlSlug(slug));
+}

Alternatively, you could use the double NOT operator for even more concise code:

-async function isExistingUrlSlug(slug: string): Promise<boolean> {
-  return (await getWorkBookByUrlSlug(slug)) ? true : false;
-}
+async function isExistingUrlSlug(slug: string): Promise<boolean> {
+  return !!(await getWorkBookByUrlSlug(slug));
+}
🧰 Tools
🪛 Biome (1.9.4)

[error] 100-100: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

src/lib/utils/workbook.ts (1)

38-38: Simplify the boolean assignment.

The ternary operator can be simplified as suggested by the static analysis tool.

Apply this diff:

-  const isExistingAuthor = workbookAuthor ? true : false;
+  const isExistingAuthor = !!workbookAuthor;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5557a55 and 4e4c797.

📒 Files selected for processing (18)
  • prisma/ERD.md (1 hunks)
  • prisma/migrations/20250531081213_add_url_slug_to_workbook/migration.sql (1 hunks)
  • prisma/migrations/migration_lock.toml (1 hunks)
  • prisma/schema.prisma (2 hunks)
  • src/lib/components/WorkBook/WorkBookForm.svelte (2 hunks)
  • src/lib/components/WorkBook/WorkBookInputFields.svelte (3 hunks)
  • src/lib/components/WorkBooks/TitleTableBodyCell.svelte (2 hunks)
  • src/lib/components/WorkBooks/WorkBookBaseTable.svelte (3 hunks)
  • src/lib/services/workbooks.ts (3 hunks)
  • src/lib/types/workbook.ts (1 hunks)
  • src/lib/utils/url.ts (1 hunks)
  • src/lib/utils/workbook.ts (3 hunks)
  • src/lib/zod/schema.ts (2 hunks)
  • src/routes/workbooks/[slug]/+page.server.ts (1 hunks)
  • src/routes/workbooks/edit/[slug]/+page.server.ts (5 hunks)
  • src/test/lib/utils/url.test.ts (2 hunks)
  • src/test/lib/utils/workbook.test.ts (2 hunks)
  • src/test/lib/zod/workbook_schema.test.ts (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
src/lib/zod/schema.ts (1)
src/lib/utils/url.ts (1)
  • isValidUrlSlug (32-42)
src/test/lib/utils/workbook.test.ts (1)
src/lib/utils/workbook.ts (1)
  • parseWorkBookUrlSlug (95-97)
src/test/lib/utils/url.test.ts (1)
src/lib/utils/url.ts (1)
  • isValidUrlSlug (32-42)
src/routes/workbooks/[slug]/+page.server.ts (2)
src/routes/workbooks/edit/[slug]/+page.server.ts (1)
  • load (18-60)
src/lib/utils/workbook.ts (2)
  • findWorkBookIdFrom (62-80)
  • getWorkbookWithAuthor (14-41)
src/test/lib/zod/workbook_schema.test.ts (2)
src/lib/types/workbook.ts (2)
  • WorkBookTasks (62-62)
  • WorkBook (15-18)
src/lib/zod/schema.ts (1)
  • workBookSchema (54-97)
src/lib/services/workbooks.ts (1)
src/lib/types/workbook.ts (1)
  • WorkBook (15-18)
src/lib/utils/workbook.ts (2)
src/lib/types/workbook.ts (1)
  • WorkBook (15-18)
src/lib/utils/url.ts (1)
  • isValidUrlSlug (32-42)
🪛 Biome (1.9.4)
src/lib/services/workbooks.ts

[error] 100-100: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

src/lib/utils/workbook.ts

[error] 40-40: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

🔇 Additional comments (29)
prisma/migrations/migration_lock.toml (1)

2-3: Minor documentation improvement looks good.

The correction from "i.e. Git" to "e.g., Git" improves grammatical accuracy, as "e.g." (for example) is more appropriate than "i.e." (that is) in this context.

prisma/ERD.md (1)

181-181: ERD update correctly reflects the new urlSlug field.

The addition of the optional urlSlug field to the workbook entity properly documents the schema change and aligns with the PR objectives.

src/lib/components/WorkBook/WorkBookForm.svelte (1)

91-91: urlSlug binding integration is correct.

The two-way data binding for urlSlug follows the established pattern and properly integrates the new field into the form.

prisma/migrations/20250531081213_add_url_slug_to_workbook/migration.sql (1)

1-12:

✅ Verification successful

Database migration looks correct with good safety measures.

The migration properly adds the optional urlSlug column with appropriate uniqueness constraints. The warning comments about potential duplicate values are helpful for deployment planning.

Consider verifying that URL slug format validation is properly implemented at the application level:


🏁 Script executed:

#!/bin/bash
# Description: Verify URL slug validation logic exists in the application code

# Check for URL slug validation functions
echo "Checking for URL slug validation logic:"
rg "urlSlug|url.*slug" --type ts --type js -A 3 -B 1

# Look for validation schemas that might include URL slug rules
echo "Checking validation schemas:"
rg "slug.*validation|validation.*slug" --type ts --type js -A 5

Length of output: 11959


🏁 Script executed:

#!/bin/bash
# Inspect URL slug validation details in the Zod schema

echo "Checking urlSlug schema definition with surrounding context:"
rg "urlSlug" -n src/lib/zod/schema.ts -A5 -B3

echo
echo "Searching for regex constraints in schema.ts:"
rg "regex" -n src/lib/zod/schema.ts

echo
echo "Searching for refine calls in schema.ts:"
rg "refine" -n src/lib/zod/schema.ts

Length of output: 1249


Approve migration; URL slug validation confirmed.

The migration correctly adds an optional urlSlug column with a unique index. Application-level validation for urlSlug is already implemented:

  • In src/lib/zod/schema.ts, urlSlug is defined as .string().nullable().optional() with a .refine((v) => v === undefined || isValidUrlSlug(v), { … }) check (backed by a regex in isValidUrlSlug).
  • Comprehensive tests in src/test/lib/zod/workbook_schema.test.ts cover valid and invalid slug scenarios.
src/lib/components/WorkBooks/TitleTableBodyCell.svelte (1)

27-27: LGTM! URL generation correctly uses slug when available.

The href properly uses the new helper function to generate URLs with slugs when available, falling back to numeric IDs.

src/lib/types/workbook.ts (1)

11-11: LGTM! Well-documented optional field addition.

The type definition correctly adds the optional urlSlug field with proper typing (string | null) and includes comprehensive documentation in the comment explaining the purpose and format restrictions.

src/lib/utils/url.ts (1)

17-42: LGTM! Excellent slug validation implementation.

The validation function is well-implemented with:

  • Comprehensive JSDoc documentation
  • Proper regex validation for allowed characters and structure
  • Prevention of purely numeric slugs (avoiding conflicts with numeric IDs)
  • Clear separation of concerns with two distinct validation checks

The regex pattern correctly enforces the requirements: lowercase letters, numbers, hyphens (but not consecutive), and ensures proper start/end characters.

src/lib/components/WorkBooks/WorkBookBaseTable.svelte (2)

13-13: LGTM! Import addition is correct.

The addition of WorkbookList to the imports is necessary for the new helper function.


132-132: LGTM! Edit link correctly uses slug-based URL.

The edit link properly uses the helper function to generate URLs with slugs when available.

src/lib/zod/schema.ts (2)

7-7: LGTM: Import statement correctly adds URL slug validation.

The import of isValidUrlSlug is properly added to support the new validation requirements.


73-92: LGTM: Well-structured URL slug validation with proper edge case handling.

The validation logic is comprehensive and handles all necessary cases:

  • Correctly allows empty/null/undefined values and transforms them to undefined
  • Enforces 30-character limit with appropriate error message
  • Transforms to lowercase to ensure consistency
  • Uses the dedicated isValidUrlSlug validator for format checking
  • Provides clear Japanese error messages for users

The validation chain order is correct: length check → transform → format validation.

prisma/schema.prisma (2)

184-184: LGTM: Well-designed database schema addition.

The urlSlug field is properly implemented:

  • Optional (String?) allows existing records to remain valid
  • @unique constraint ensures no duplicate slugs
  • Excellent documentation comment with clear examples and character restrictions
  • Field placement is logical within the WorkBook model

235-235: LGTM: Documentation update is accurate.

The comment update appropriately reflects that grading criteria are now publicly available.

src/lib/components/WorkBook/WorkBookInputFields.svelte (3)

16-16: LGTM: Proper TypeScript typing for the new property.

The urlSlug property is correctly typed as optional string | null to match the schema definition.


32-32: LGTM: Bindable property with appropriate default.

The bindable declaration with undefined default properly handles the optional nature of the URL slug field.


149-160: LGTM: Secure admin-only input field with clear constraints.

The implementation correctly:

  • Restricts visibility and editing to administrators only
  • Uses conditional input type (hidden for non-admins)
  • Provides clear label with character and length constraints
  • Properly binds the value for two-way data binding
  • Displays validation errors appropriately

This ensures proper access control while maintaining form functionality.

src/lib/services/workbooks.ts (3)

40-62: LGTM: Well-implemented slug-based retrieval function.

The getWorkBookByUrlSlug function correctly:

  • Follows the same pattern as getWorkBook for consistency
  • Includes proper JSDoc documentation with examples
  • Uses findUnique with the slug field for efficient lookup
  • Includes related workBookTasks ordered by priority
  • Returns the same type as other workbook retrieval functions

67-71: LGTM: Proper uniqueness validation prevents duplicate slugs.

The slug existence check correctly:

  • Only validates when a slug is provided (handles optional nature)
  • Throws descriptive error for duplicate slugs
  • Prevents database constraint violations

86-86: LGTM: URL slug properly included in workbook creation.

The urlSlug field is correctly added to the create operation data.

src/routes/workbooks/edit/[slug]/+page.server.ts (2)

21-22: LGTM! Consistent with simplified approach.

The load function correctly uses getWorkbookWithAuthor(slug) directly, letting it handle slug resolution internally. This is clean and avoids redundant calls.


90-96: Excellent improvement in error handling!

Changing from error() to fail() for form validation errors is a great UX improvement. This allows the form to be re-rendered with error messages instead of throwing a server error, providing better user feedback.

src/test/lib/utils/url.test.ts (1)

64-131: Excellent comprehensive test coverage!

The test suite for isValidUrlSlug is thorough and well-structured:

  • Valid cases: Covers single characters, multiple lengths, algorithm names, and mixed alphanumeric with hyphens
  • Invalid cases: Covers empty strings, leading/trailing hyphens, consecutive hyphens, uppercase letters, special characters, spaces, purely numeric strings, and full-width characters
  • Edge cases: Properly tests boundary conditions like purely numeric slugs which should be invalid

The tests align perfectly with the validation rules in the isValidUrlSlug function and follow the existing test pattern consistently.

src/test/lib/utils/workbook.test.ts (1)

94-136: Well-structured test coverage for URL slug parsing!

The test suite for parseWorkBookUrlSlug follows the established pattern and provides good coverage:

  • Valid cases: Tests various valid slug formats that should return the slug unchanged
  • Invalid cases: Covers empty strings, spaces, special characters, uppercase letters, underscores, and malformed hyphens
  • Consistency: Uses the same test helper pattern as existing parseWorkBookId tests

The tests properly validate that the function returns the slug for valid inputs and null for invalid inputs, matching the expected behavior.

src/test/lib/zod/workbook_schema.test.ts (4)

16-16: LGTM! URL slug field addition is well-documented.

The addition of the optional urlSlug field with a clear comment explaining its purpose and constraints is excellent. The comment provides good examples and specifies the allowed characters.


20-37: Excellent refactoring with helper functions.

The introduction of createWorkBookBase and createWorkBookTasks helper functions significantly improves code maintainability and reduces duplication. The use of the spread operator for overrides makes the API clean and flexible.


83-151: Comprehensive test coverage for valid URL slug scenarios.

The test cases cover all important valid scenarios including single character, hyphenated combinations, numbers with letters, edge cases with empty/null/undefined values, and the 30-character limit. This aligns well with the validation requirements.


302-360: Thorough coverage of invalid URL slug edge cases.

The invalid test cases properly cover critical edge cases including:

  • Length validation (31+ characters)
  • Numeric-only strings (which should be invalid per the validation logic)
  • Invalid characters (spaces)
  • Invalid hyphen patterns (standalone hyphens, multiple consecutive hyphens)

This ensures the validation is robust and prevents common mistakes.

src/lib/utils/workbook.ts (2)

14-41: Good refactoring to support both ID and URL slug lookup.

The function now properly handles both numeric IDs and URL slugs by attempting to parse each type and fetching accordingly. The error handling maintains the original behavior while supporting the new functionality.

🧰 Tools
🪛 Biome (1.9.4)

[error] 40-40: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


95-97: Clean and focused URL slug validation function.

The function properly delegates to the validation utility and has a clear, simple implementation. Good separation of concerns.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/routes/workbooks/edit/[slug]/+page.svelte (1)

44-60: Enhanced error display improves user experience.

The error display has been significantly improved with:

  • Responsive, centered layout with proper spacing
  • Semantic heading structure for accessibility
  • Dark mode support throughout
  • Clear navigation back to workbooks page

The implementation follows good practices for responsive design and accessibility. The TODO comment correctly identifies that this could be extracted into a reusable error component for better code organization.

Consider extracting this error display into a reusable component as indicated by the TODO comment:

<!-- ErrorDisplay.svelte -->
<script lang="ts">
  import { Heading, Button } from 'svelte-5-ui-lib';
  import HeadingOne from '$lib/components/HeadingOne.svelte';
  
  let { status, message, returnUrl, returnLabel } = $props<{
    status: number;
    message: string;
    returnUrl: string;
    returnLabel: string;
  }>();
</script>

<div class="container mx-auto md:w-4/5 lg:w-2/3 py-4 md:py-8 px-3 md:px-0 flex flex-col items-center">
  <HeadingOne title="エラーが発生しました" />
  
  <Heading tag="h2" class="text-3xl mb-3 text-gray-900 dark:text-gray-300">
    {status}
  </Heading>
  
  <p class="dark:text-gray-300">{message}</p>
  
  <div class="flex justify-center mt-6">
    <Button href={returnUrl} color="primary" class="px-6">
      {returnLabel}
    </Button>
  </div>
</div>
src/test/lib/utils/workbooks.test.ts (1)

7-22: LGTM: Well-designed helper function with minor suggestion.

The createWorkBookListBase helper function is well-implemented:

  • Provides sensible defaults for all required properties
  • Supports partial overrides for flexible test scenarios
  • Makes test code more maintainable and readable

Consider adding urlSlug: undefined to the default object for clarity:

function createWorkBookListBase(overrides: Partial<WorkbookList> = {}): WorkbookList {
  return {
    id: 1,
    authorId: '3',
    authorName: 'Alice',
    title: '実装力を鍛える問題集',
    description: '',
    editorialUrl: '',
    isPublished: false,
    isOfficial: false,
    isReplenished: false,
    workBookType: WorkBookType.CREATED_BY_USER,
    workBookTasks: [],
+   urlSlug: undefined,
    ...overrides,
  };
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between ee6f171 and c80232d.

📒 Files selected for processing (9)
  • src/lib/components/WorkBooks/TitleTableBodyCell.svelte (2 hunks)
  • src/lib/components/WorkBooks/WorkBookBaseTable.svelte (2 hunks)
  • src/lib/services/workbooks.ts (3 hunks)
  • src/lib/utils/workbook.ts (3 hunks)
  • src/lib/utils/workbooks.ts (1 hunks)
  • src/routes/workbooks/[slug]/+page.server.ts (1 hunks)
  • src/routes/workbooks/edit/[slug]/+page.server.ts (3 hunks)
  • src/routes/workbooks/edit/[slug]/+page.svelte (2 hunks)
  • src/test/lib/utils/workbooks.test.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/lib/components/WorkBooks/TitleTableBodyCell.svelte
  • src/lib/components/WorkBooks/WorkBookBaseTable.svelte
  • src/routes/workbooks/edit/[slug]/+page.server.ts
  • src/routes/workbooks/[slug]/+page.server.ts
  • src/lib/services/workbooks.ts
  • src/lib/utils/workbook.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/lib/utils/workbooks.ts (1)
src/lib/types/workbook.ts (1)
  • WorkbookList (28-32)
src/test/lib/utils/workbooks.test.ts (2)
src/lib/types/workbook.ts (1)
  • WorkbookList (28-32)
src/lib/utils/workbooks.ts (1)
  • getUrlSlugFrom (17-21)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: preview
🔇 Additional comments (5)
src/routes/workbooks/edit/[slug]/+page.svelte (1)

3-3: LGTM! Import additions support the UI enhancements.

The new imports for UI components (Heading, Button, HeadingOne) and the navigation constant (WORKBOOKS_PAGE) are appropriate and necessary for the enhanced error display implementation.

Also applies to: 5-5, 12-12

src/lib/utils/workbooks.ts (2)

2-2: LGTM: Clean import addition.

The import of WorkbookList type is correctly added for the new function.


11-21: LGTM: Well-implemented utility function with comprehensive documentation.

The getUrlSlugFrom function is correctly implemented with:

  • Clear JSDoc documentation explaining purpose, parameters, and return value
  • Proper fallback logic that handles all falsy values (null, undefined, empty string)
  • Clean and readable implementation

The function will correctly return the URL slug when available, or gracefully fall back to the workbook ID as a string.

src/test/lib/utils/workbooks.test.ts (2)

3-5: LGTM: Proper imports for new functionality.

The imports correctly include the new getUrlSlugFrom function and necessary types.


47-67: LGTM: Comprehensive test coverage for the utility function.

The test suite thoroughly covers all important scenarios:

  • ✅ Returns urlSlug when it exists and is truthy
  • ✅ Returns id as string when urlSlug is null
  • ✅ Returns id as string when urlSlug is undefined
  • ✅ Returns id as string when urlSlug is empty string

The test cases correctly verify the function's fallback behavior and use appropriate assertions. This ensures the getUrlSlugFrom function will work reliably across all expected input scenarios.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/lib/components/Messages/ErrorMessageAndReturnButton.svelte (2)

15-31: Consider accessibility improvements.

The component could benefit from semantic HTML and ARIA attributes for better screen reader support.

Consider these accessibility enhancements:

-<div
-  class="container mx-auto md:w-4/5 lg:w-2/3 py-4 md:py-8 px-3 md:px-0 flex flex-col items-center"
->
+<main
+  class="container mx-auto md:w-4/5 lg:w-2/3 py-4 md:py-8 px-3 md:px-0 flex flex-col items-center"
+  role="alert"
+  aria-live="polite"
+>
   <HeadingOne title="エラーが発生しました" />

-  <Heading tag="h2" class="text-3xl mb-3 text-gray-900 dark:text-gray-300">
+  <Heading tag="h2" class="text-3xl mb-3 text-gray-900 dark:text-gray-300" aria-label="エラーコード">
     {errorStatus ?? ''}
   </Heading>

-  <p class="dark:text-gray-300">{errorMessage ?? ''}</p>
+  <p class="dark:text-gray-300" aria-label="エラーメッセージ">{errorMessage ?? ''}</p>

18-18: Consider internationalization for hard-coded text.

The component uses hard-coded Japanese text which may limit reusability across different locales.

Consider accepting the error title as a prop or using an i18n solution:

   interface Props {
     errorStatus: number | undefined;
     errorMessage: string | undefined;
     returnUrl: string;
     returnButtonLabel: string;
+    errorTitle?: string;
   }

-  let { errorStatus, errorMessage, returnUrl, returnButtonLabel }: Props = $props();
+  let { errorStatus, errorMessage, returnUrl, returnButtonLabel, errorTitle = "エラーが発生しました" }: Props = $props();

-  <HeadingOne title="エラーが発生しました" />
+  <HeadingOne title={errorTitle} />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between c80232d and 66081b7.

📒 Files selected for processing (4)
  • src/lib/components/Messages/ErrorMessageAndReturnButton.svelte (1 hunks)
  • src/routes/workbooks/+page.svelte (1 hunks)
  • src/routes/workbooks/edit/[slug]/+page.svelte (2 hunks)
  • src/test/lib/utils/workbooks.test.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/routes/workbooks/+page.svelte
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/test/lib/utils/workbooks.test.ts
  • src/routes/workbooks/edit/[slug]/+page.svelte
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: preview

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/routes/sitemap.xml/+server.ts (2)

45-48: URL slug implementation looks good with minor type compatibility consideration.

The implementation correctly uses getUrlSlugFrom to generate URL slugs for the sitemap, which improves SEO by using human-readable URLs instead of numeric IDs. The fallback to ID ensures backward compatibility.

The workaround with authorName: '' to satisfy the WorkbookList type is functional but indicates a potential type design issue.

Consider creating a more specific utility function or type to avoid the type workaround:

-        return getUrlSlugFrom({ ...workbook, authorName: '' });
+        return getUrlSlugFrom(workbook);

This would require updating the getUrlSlugFrom function to accept a more flexible type that doesn't require authorName.


28-29: Update documentation example to reflect URL slug usage.

The example in the JSDoc comment still shows a numeric ID (123) in the URL, but the implementation now uses URL slugs.

- *     <loc>https://atcoder-novisteps.vercel.app/workbooks/123</loc>
+ *     <loc>https://atcoder-novisteps.vercel.app/workbooks/beginner-algorithms</loc>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 15fffa6 and 958ed6c.

📒 Files selected for processing (1)
  • src/routes/sitemap.xml/+server.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/routes/sitemap.xml/+server.ts (1)
src/lib/utils/workbooks.ts (1)
  • getUrlSlugFrom (17-21)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: preview
🔇 Additional comments (1)
src/routes/sitemap.xml/+server.ts (1)

15-15: LGTM: Import addition is appropriate.

The import of getUrlSlugFrom utility function is correctly added to support the new URL slug functionality.

@KATO-Hiro KATO-Hiro merged commit 085de44 into staging Jun 5, 2025
3 checks passed
@KATO-Hiro KATO-Hiro deleted the #2020 branch June 5, 2025 10:02
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.

[URL] 問題集の詳細ページを連番から作成者が文字列を指定できるようにしましょう
1 participant