-
Notifications
You must be signed in to change notification settings - Fork 8
feat: blink card invitations #353
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
base: main
Are you sure you want to change the base?
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a invitations and templates feature to the admin panel: list/new/detail pages, template list/edit pages, mock data and types, multiple UI components (cards, modal, badge), shared primitives (button, form controls, pagination), a date formatter, sidebar routes, and a sidebar-hide CSS rule. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a new visa card invitations feature to the admin panel, allowing administrators to send and manage invitation notifications. The implementation includes invitation list management with filtering capabilities, template-based invitation creation, and reusable UI components for forms and pagination.
Key Changes
- Added invitation and template management pages with filtering, pagination, and CRUD operations
- Created reusable UI components (Button, form controls, Pagination) for consistent styling and behavior
- Implemented mock data structure for invitations and templates to support development before API integration
Reviewed changes
Copilot reviewed 17 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/admin-panel/components/side-bar.tsx | Added "Invitations" and "Templates" navigation menu items with corresponding icons |
| apps/admin-panel/components/shared/pagination/types.ts | Defined TypeScript types for pagination component props and page change payload |
| apps/admin-panel/components/shared/pagination/pagination.tsx | Implemented reusable pagination component with page navigation and state management |
| apps/admin-panel/components/shared/pagination/index.ts | Barrel export for pagination module |
| apps/admin-panel/components/shared/form-controls/types.ts | Defined TypeScript types for text input, select, textarea, and checkbox components |
| apps/admin-panel/components/shared/form-controls/index.ts | Barrel export for form controls module |
| apps/admin-panel/components/shared/form-controls/form-controls.tsx | Implemented reusable form control components with consistent styling |
| apps/admin-panel/components/shared/button/types.ts | Defined button component props with variant support |
| apps/admin-panel/components/shared/button/index.ts | Barrel export for button module |
| apps/admin-panel/components/shared/button/button.tsx | Implemented reusable button component with primary and outline variants |
| apps/admin-panel/components/invitations/status-badge.tsx | Created status badge component for displaying invitation states |
| apps/admin-panel/app/utils.ts | Added date formatting utility for consistent date display |
| apps/admin-panel/app/templates/page.tsx | Implemented templates listing page with table view and pagination |
| apps/admin-panel/app/mock-data.ts | Created mock data for invitations and templates during development |
| apps/admin-panel/app/invitations/types.ts | Defined invitation and template types and enums |
| apps/admin-panel/app/invitations/page.tsx | Implemented invitations listing page with filtering, search, and pagination |
| apps/admin-panel/app/invitations/new/page.tsx | Created invitation creation page with template selection and customization |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export default function TemplatesPage() { | ||
| const router = useRouter() | ||
| const [pageItems, setPageItems] = useState<TemplateRow[]>(visaTemplatesMock.slice(0, 0)) |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial state uses slice(0, 0) which returns an empty array. This should be slice(0, DEFAULT_PAGE_SIZE) or rely on the initial page change to populate items, but the current implementation may cause the first page to appear empty until pagination triggers.
| export default function TemplatesPage() { | |
| const router = useRouter() | |
| const [pageItems, setPageItems] = useState<TemplateRow[]>(visaTemplatesMock.slice(0, 0)) | |
| const DEFAULT_PAGE_SIZE = 10; | |
| export default function TemplatesPage() { | |
| const router = useRouter() | |
| const [pageItems, setPageItems] = useState<TemplateRow[]>(visaTemplatesMock.slice(0, DEFAULT_PAGE_SIZE)) |
| export const TemplateIcon = { | ||
| Star: "star", | ||
| Check: "check", | ||
| } | ||
|
|
||
| export type TemplateIcon = (typeof TemplateIcon)[keyof typeof TemplateIcon] |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The constant and type share the same name TemplateIcon, which can cause confusion. Consider renaming the constant to TemplateIcons (plural) or TemplateIconValues to differentiate it from the type.
| export const TemplateIcon = { | |
| Star: "star", | |
| Check: "check", | |
| } | |
| export type TemplateIcon = (typeof TemplateIcon)[keyof typeof TemplateIcon] | |
| export const TemplateIcons = { | |
| Star: "star", | |
| Check: "check", | |
| } | |
| export type TemplateIcon = (typeof TemplateIcons)[keyof typeof TemplateIcons] |
There was a problem hiding this 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
♻️ Duplicate comments (1)
apps/admin-panel/app/utils.ts (1)
31-31: Remove the redundant replace operation.This line replaces a space with itself, which is redundant and serves no purpose. The operation can be safely removed.
Apply this diff to remove the redundant operation:
export const formatDateDisplay = (dateString: string): string => { return new Date(`${dateString}T00:00:00Z`) .toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric", }) - .replace(/ /g, " ") }
🧹 Nitpick comments (4)
apps/admin-panel/app/utils.ts (1)
24-32: Consider adding input validation and error handling.The function assumes a valid date string format (YYYY-MM-DD) and will return "Invalid Date" if the input is malformed. Consider adding validation or error handling if the input source is not strictly controlled.
Example with validation:
export const formatDateDisplay = (dateString: string): string => { const date = new Date(`${dateString}T00:00:00Z`) if (isNaN(date.getTime())) { throw new Error(`Invalid date string: ${dateString}`) } return date.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric", }) }apps/admin-panel/components/shared/pagination/pagination.tsx (1)
52-75: Optional: reuse shared Button for Previous/Next to avoid style duplication.You’re re-declaring button classes here that are very close to the shared Button’s base styles. Consider swapping these
<button>s for the shared<Button>component (perhaps with a lighter variant) to keep styling consistent and reduce duplication.apps/admin-panel/app/templates/page.tsx (1)
15-20: AlignhandlePageChangeparameter type with the shared pagination payload.Right now the callback is typed as
{ offset: number; limit: number }, butPaginationis documented to call with a richer payload. To keep types in sync and avoid surprises ifPageChangePayloadchanges, consider:-import { Pagination } from "../../components/shared/pagination" +import { Pagination } from "../../components/shared/pagination" +import type { PageChangePayload } from "../../components/shared/pagination/types" - const handlePageChange = useCallback( - ({ offset, limit }: { offset: number; limit: number }) => { + const handlePageChange = useCallback( + ({ offset, limit }: PageChangePayload) => { setPageItems(visaTemplatesMock.slice(offset, offset + limit)) }, - [], + [], )apps/admin-panel/app/invitations/new/page.tsx (1)
15-167: New invitation flow is coherent; toggles are ready for backend integration.State management for user, template selection, and
canSendgating looks good. Once you wire this to a real API, remember to passsendPush,addHistory, andaddBulletinthrough to the request so those toggles have an effect.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
apps/admin-panel/components/icons/fileText.svgis excluded by!**/*.svgapps/admin-panel/components/icons/send.svgis excluded by!**/*.svg
📒 Files selected for processing (17)
apps/admin-panel/app/invitations/new/page.tsx(1 hunks)apps/admin-panel/app/invitations/page.tsx(1 hunks)apps/admin-panel/app/invitations/types.ts(1 hunks)apps/admin-panel/app/mock-data.ts(1 hunks)apps/admin-panel/app/templates/page.tsx(1 hunks)apps/admin-panel/app/utils.ts(1 hunks)apps/admin-panel/components/invitations/status-badge.tsx(1 hunks)apps/admin-panel/components/shared/button/button.tsx(1 hunks)apps/admin-panel/components/shared/button/index.ts(1 hunks)apps/admin-panel/components/shared/button/types.ts(1 hunks)apps/admin-panel/components/shared/form-controls/form-controls.tsx(1 hunks)apps/admin-panel/components/shared/form-controls/index.ts(1 hunks)apps/admin-panel/components/shared/form-controls/types.ts(1 hunks)apps/admin-panel/components/shared/pagination/index.ts(1 hunks)apps/admin-panel/components/shared/pagination/pagination.tsx(1 hunks)apps/admin-panel/components/shared/pagination/types.ts(1 hunks)apps/admin-panel/components/side-bar.tsx(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
apps/admin-panel/components/shared/button/button.tsx (1)
apps/admin-panel/components/shared/button/types.ts (2)
ButtonVariant(3-3)ButtonProps(5-7)
apps/admin-panel/app/invitations/new/page.tsx (2)
apps/admin-panel/app/mock-data.ts (1)
visaTemplatesMock(70-111)apps/admin-panel/components/shared/form-controls/form-controls.tsx (4)
TextInput(11-14)SelectInput(16-23)TextArea(25-28)Checkbox(30-33)
apps/admin-panel/components/shared/pagination/pagination.tsx (1)
apps/admin-panel/components/shared/pagination/types.ts (1)
PaginationProps(10-15)
apps/admin-panel/app/templates/page.tsx (3)
apps/admin-panel/app/invitations/types.ts (1)
TemplateRow(27-34)apps/admin-panel/app/mock-data.ts (1)
visaTemplatesMock(70-111)apps/admin-panel/components/shared/pagination/pagination.tsx (1)
Pagination(9-77)
apps/admin-panel/components/invitations/status-badge.tsx (1)
apps/admin-panel/app/invitations/types.ts (1)
InvitationStatus(1-1)
apps/admin-panel/app/mock-data.ts (1)
apps/admin-panel/app/invitations/types.ts (2)
InvitationRow(3-8)TemplateRow(27-34)
apps/admin-panel/components/shared/form-controls/form-controls.tsx (1)
apps/admin-panel/components/shared/form-controls/types.ts (4)
TextInputProps(7-7)SelectInputProps(8-8)TextAreaProps(9-9)CheckboxProps(10-10)
🪛 GitHub Actions: Spelling
apps/admin-panel/app/mock-data.ts
[error] 100-100: typos: 'ist' should be 'is', 'it', 'its', 'sit', or 'list'. Command './typos . --config typos.toml' exited with code 2.
[error] 101-101: typos: 'Sie' should be 'Size', 'Sigh', or 'Side'. Command './typos . --config typos.toml' exited with code 2.
[warning] 1-1: typos: potential typos detected on lines 100-101. Review German strings for proper capitalization/terms. Command './typos . --config typos.toml' exited with code 2.
🪛 GitHub Check: CodeQL
apps/admin-panel/app/utils.ts
[warning] 31-31: Replacement of a substring with itself
This replaces ' ' with itself.
🪛 GitHub Check: Spell Check with Typos
apps/admin-panel/app/mock-data.ts
[warning] 101-101:
"Sie" should be "Size" or "Sigh" or "Side".
[warning] 100-100:
"ist" should be "is" or "it" or "its" or "sit" or "list".
🔇 Additional comments (17)
apps/admin-panel/components/shared/pagination/types.ts (1)
1-15: LGTM!The pagination types are well-structured and comprehensive. The
PageChangePayloadincludes all necessary pagination state fields, andPaginationPropsprovides a clean, flexible API with required callback and optional configuration.apps/admin-panel/components/shared/form-controls/form-controls.tsx (2)
30-33: LGTM! Correct prop spreading order.The
type="checkbox"attribute is correctly placed after{...rest}to ensure the input type cannot be overridden accidentally via props. This is the proper defensive pattern for a dedicated Checkbox component.
1-33: Well-structured form control components.The implementation follows a consistent pattern across all form controls with appropriate base styling and proper prop spreading. The components provide a clean, reusable API for form inputs throughout the admin panel.
apps/admin-panel/components/shared/form-controls/types.ts (1)
1-10: LGTM!Clean type definitions that leverage React's built-in HTML attribute types. The semantic naming improves code readability while maintaining full type safety.
apps/admin-panel/components/shared/form-controls/index.ts (1)
1-1: LGTM!Standard barrel export pattern for aggregating the form-controls public API.
apps/admin-panel/components/shared/button/index.ts (1)
1-1: LGTM!Standard barrel export pattern consistent with other shared components.
apps/admin-panel/components/shared/pagination/index.ts (1)
1-1: LGTM!Standard barrel export pattern for the pagination module.
apps/admin-panel/components/side-bar.tsx (2)
14-15: LGTM!Icon imports follow the existing pattern and are appropriately named for the new navigation items.
38-47: LGTM!The new Invitations and Templates routes are properly integrated following the existing structure. The route objects match the established pattern with appropriate icons and paths.
apps/admin-panel/components/shared/button/types.ts (1)
3-7: Button type definitions look good.
ButtonVariantandButtonPropsare minimal, type-safe, and match the Button implementation.apps/admin-panel/components/shared/button/button.tsx (1)
5-22: Shared Button implementation is solid.Good use of variant-based classes, safe default
type="button", and className merging; no issues spotted.apps/admin-panel/components/invitations/status-badge.tsx (1)
5-37: InvitationStatusBadge is concise and type-safe.The status-to-meta mapping and optional label override are straightforward and correct.
apps/admin-panel/components/shared/pagination/pagination.tsx (1)
15-42: Pagination state and payload logic look correct.The clamping via
safePage, reset behavior viaresetKey, and computedoffset/limitpayload are all consistent and robust, including the 0-items case.apps/admin-panel/app/templates/page.tsx (1)
26-29: Please verify that/templates/newis implemented.The “Create New Template” button navigates to
/templates/new. If that route/page isn’t implemented in this PR or already present in the app, this will currently 404.apps/admin-panel/app/invitations/page.tsx (1)
26-36: [Rewritten review comment]
[Classification tag]apps/admin-panel/app/invitations/types.ts (2)
1-19: Invitation status and filter modelling looks solidThe
InvitationStatus,InvitationRow, andInvitationStatusOptions/StatusFiltercombo is clear and idiomatic: literal union for status, a row shape that matches likely table usage, and a filter type derived from the options object so it stays in sync with the UI.
20-34: Addas constto preserveTemplateIconas a literal union typeThe
TemplateIconobject lacksas const, causing TypeScript to widen its properties tostringinstead of the literal union"star" | "check". This removes type safety and auto-complete forTemplateRow["icon"]and consumers.Fix by adding
as const:export const TemplateIcon = { Star: "star", Check: "check", +} as constThis ensures the type correctly resolves to
"star" | "check"rather thanstring.
| const [pageItems, setPageItems] = useState<InvitationRow[]>([]) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initial empty-state flicker and copy wording.
- Because
pageItemsstarts as[], the table briefly shows “No users found.” until the firstonPageChangeruns. SeedingpageItemswith the first page (similar to the Templates page suggestion) would avoid this. - The empty-state message says “No users found.” while the page is titled “Invitations”; consider “No invitations found.” for clearer wording.
Also applies to: 60-62, 160-164
| <Button | ||
| variant="outline-blue" | ||
| onClick={() => handleRowClick(invitation.id)} | ||
| > | ||
| View | ||
| </Button> | ||
| {invitation.status === InvitationStatusOptions.Pending && ( | ||
| <Button variant="outline-red">Revoke</Button> | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revoke button is inert; consider wiring it or making it clearly disabled.
The Revoke button is rendered only for pending invitations but has no onClick handler. From a user’s perspective it looks actionable yet does nothing. Either hook it up to the intended revoke flow (even a stub handler/toast for now) or hide/disable it until backend support is ready.
| id: "blink-private-de", | ||
| name: "Blink Private Invite (DE)", | ||
| language: "German", | ||
| icon: "star", | ||
| title: "Blink Private ist da!", | ||
| body: "Melden Sie sich an, um Ihre Visa-Karte und mehr zu erhalten", | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typos check is failing on valid German strings.
The typos pipeline is flagging "ist" and "Sie" here, even though the German copy is correct. Consider adding these words (or this file/path) to the typos.toml allowlist/ignore rules so the pipeline passes without degrading the translations, especially since this file is explicitly temporary mock data.
🧰 Tools
🪛 GitHub Actions: Spelling
[error] 100-100: typos: 'ist' should be 'is', 'it', 'its', 'sit', or 'list'. Command './typos . --config typos.toml' exited with code 2.
[error] 101-101: typos: 'Sie' should be 'Size', 'Sigh', or 'Side'. Command './typos . --config typos.toml' exited with code 2.
🪛 GitHub Check: Spell Check with Typos
[warning] 101-101:
"Sie" should be "Size" or "Sigh" or "Side".
[warning] 100-100:
"ist" should be "is" or "it" or "its" or "sit" or "list".
🤖 Prompt for AI Agents
In apps/admin-panel/app/mock-data.ts around lines 96 to 102, the typos pipeline
is incorrectly flagging valid German words ("ist", "Sie"); add these words or
this file/path to the typos.toml allowlist or ignore rules so the pipeline won't
fail on this temporary mock data—update typos.toml to either add "ist" and "Sie"
to the allowed-words list or add apps/admin-panel/app/mock-data.ts (or its
directory) to the ignore patterns, commit the change, and rerun the pipeline.
| const [pageItems, setPageItems] = useState<TemplateRow[]>(visaTemplatesMock.slice(0, 0)) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid initial “No templates found.” flicker before pagination loads.
Because pageItems starts as visaTemplatesMock.slice(0, 0), the table initially renders the “No templates found.” row until the first onPageChange fires. You can seed pageItems with the first page (e.g., using DEFAULT_PAGE_SIZE) to avoid this brief misleading empty state.
Also applies to: 78-84
🤖 Prompt for AI Agents
In apps/admin-panel/app/templates/page.tsx around lines 13-14 (and also apply
same change to lines 78-84), the pageItems state is initialized to
visaTemplatesMock.slice(0, 0) which causes a brief “No templates found.”
flicker; initialize pageItems with the first page of data instead (e.g.,
visaTemplatesMock.slice(0, DEFAULT_PAGE_SIZE) or compute start/end based on
current page and DEFAULT_PAGE_SIZE) so the table renders the first page
immediately and update setPageItems logic consistently where pagination handlers
run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
♻️ Duplicate comments (2)
apps/admin-panel/app/mock-data.ts (1)
96-102: Pipeline failing on valid German strings.The typos pipeline flags "ist" and "Sie" as errors, but these are correct German words in the template content. Add these words to the
typos.tomlallowlist or exclude this mock data file from spell checking.apps/admin-panel/app/templates/page.tsx (1)
13-13: Initial empty state causes "No templates found." flicker.
visaTemplatesMock.slice(0, 0)produces an empty array, causing the empty state message to briefly appear before pagination populates the first page.
🧹 Nitpick comments (3)
apps/admin-panel/components/invitations/invitation-card.tsx (1)
43-45: Placeholder menu button has no functionality.The "⋮" button currently has no
onClickhandler. If this is intentional placeholder for future features, consider adding a TODO comment or disabling the button to indicate it's not yet functional.- <button className="rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"> + <button + disabled + className="rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50" + > ⋮ </button>apps/admin-panel/app/invitations/[id]/page.tsx (2)
90-92: Extract magic number to a named constant.
86400000is milliseconds in a day. Extract to a constant for clarity.+const ONE_DAY_MS = 86400000 + // Then in the useEffect: - new Date(foundInvitation.lastActivity).getTime() + 86400000, + new Date(foundInvitation.lastActivity).getTime() + ONE_DAY_MS,
147-150:handleRevokeInvitationis defined but never used.This function is not referenced anywhere in the component. Either remove it or wire it up to a UI element.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
apps/admin-panel/app/globals.css(1 hunks)apps/admin-panel/app/invitations/[id]/page.tsx(1 hunks)apps/admin-panel/app/invitations/types.ts(1 hunks)apps/admin-panel/app/mock-data.ts(1 hunks)apps/admin-panel/app/templates/[id]/edit/page.tsx(1 hunks)apps/admin-panel/app/templates/page.tsx(1 hunks)apps/admin-panel/components/invitations/change-status-modal.tsx(1 hunks)apps/admin-panel/components/invitations/client-info-card.tsx(1 hunks)apps/admin-panel/components/invitations/invitation-card.tsx(1 hunks)apps/admin-panel/components/invitations/status-badge.tsx(1 hunks)apps/admin-panel/components/invitations/status-history-card.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/admin-panel/components/invitations/status-badge.tsx
- apps/admin-panel/app/invitations/types.ts
🧰 Additional context used
🧬 Code graph analysis (8)
apps/admin-panel/app/invitations/[id]/page.tsx (2)
apps/admin-panel/app/invitations/types.ts (5)
EditableContent(46-49)InvitationRow(3-8)TemplateRow(29-36)Event(38-44)InvitationStatus(1-1)apps/admin-panel/app/mock-data.ts (2)
visaInvitationsMock(7-68)visaTemplatesMock(70-111)
apps/admin-panel/components/invitations/change-status-modal.tsx (1)
apps/admin-panel/app/invitations/types.ts (2)
InvitationStatus(1-1)TemplateRow(29-36)
apps/admin-panel/components/invitations/status-history-card.tsx (1)
apps/admin-panel/app/invitations/types.ts (1)
Event(38-44)
apps/admin-panel/app/templates/[id]/edit/page.tsx (2)
apps/admin-panel/app/invitations/types.ts (1)
TemplateRow(29-36)apps/admin-panel/app/mock-data.ts (1)
visaTemplatesMock(70-111)
apps/admin-panel/components/invitations/client-info-card.tsx (2)
apps/admin-panel/app/invitations/types.ts (1)
InvitationRow(3-8)apps/admin-panel/components/invitations/status-badge.tsx (1)
InvitationStatusBadge(32-42)
apps/admin-panel/components/invitations/invitation-card.tsx (1)
apps/admin-panel/app/invitations/types.ts (1)
EditableContent(46-49)
apps/admin-panel/app/templates/page.tsx (3)
apps/admin-panel/app/invitations/types.ts (3)
TemplateRow(29-36)TemplateIcon(21-25)TemplateIcon(27-27)apps/admin-panel/app/mock-data.ts (1)
visaTemplatesMock(70-111)apps/admin-panel/components/shared/pagination/pagination.tsx (1)
Pagination(9-77)
apps/admin-panel/app/mock-data.ts (1)
apps/admin-panel/app/invitations/types.ts (2)
InvitationRow(3-8)TemplateRow(29-36)
🪛 GitHub Actions: Spelling
apps/admin-panel/app/mock-data.ts
[warning] 100-100: "ist" should be "is" or "it" or "its" or "sit" or "list"
[error] 100-100: ist should be is, it, its, sit, list
[warning] 101-101: "Sie" should be "Size" or "Sigh" or "Side"
[error] 101-101: Sie should be Size, Sigh, or Side
🪛 GitHub Check: Spell Check with Typos
apps/admin-panel/app/mock-data.ts
[warning] 101-101:
"Sie" should be "Size" or "Sigh" or "Side".
[warning] 100-100:
"ist" should be "is" or "it" or "its" or "sit" or "list".
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: build and test admin-panel
- GitHub Check: execute via buck2
- GitHub Check: Migrate Mongodb
- GitHub Check: Check SDLs
- GitHub Check: execute via bats
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
apps/admin-panel/components/invitations/invitation-card.tsx (1)
15-55: LGTM!The component correctly handles nullable
editableContentwith optional chaining for both the preview display and the editing form inputs.apps/admin-panel/app/globals.css (1)
55-59: LGTM!The CSS rule provides a clean mechanism for full-screen modal views to hide the sidebar, with a clear explanatory comment.
apps/admin-panel/components/invitations/change-status-modal.tsx (1)
40-50: LGTM!Good modal structure with accessible backdrop click-to-close and clear header with close button.
apps/admin-panel/app/invitations/[id]/page.tsx (3)
45-59: LGTM!State initialization is well-structured with proper typing, and the dynamic route parameter handling correctly accounts for Next.js potentially returning an array.
25-43: Verify duplication with existing date formatting utilities.The review suggests these
formatDateandformatDateTimefunctions may duplicate aformatDateDisplayutility inapps/admin-panel/app/utils.ts. Confirm whether this utility exists, compare implementations, and consolidate if the functions are redundant.
6-16: DuplicateEditableContenttype definition.
EditableContentis imported from../typeson line 6, but then redefined locally on lines 13-16. Remove the local definition to avoid redundancy and potential drift between definitions.import type { Event, InvitationRow, TemplateRow, InvitationStatus } from "../types" +import type { EditableContent } from "../types" import { InvitationCard } from "../../../components/invitations/invitation-card" import { ClientInfoCard } from "../../../components/invitations/client-info-card" import { StatusHistoryCard } from "../../../components/invitations/status-history-card" import { ChangeStatusModal } from "../../../components/invitations/change-status-modal" -type EditableContent = { - title: string - body: string -} | null - const InvitationStatuses = {Likely an incorrect or invalid review comment.
| const InvitationStatuses = { | ||
| pending: { label: "Pending", color: "bg-yellow-100 text-yellow-800" }, | ||
| accepted: { label: "Accepted", color: "bg-green-100 text-green-800" }, | ||
| revoked: { label: "Revoked", color: "bg-red-100 text-red-800" }, | ||
| active: { label: "Active", color: "bg-red-100 text-red-800" }, | ||
| } as const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Active" status uses the same color as "Revoked".
Line 22 assigns bg-red-100 text-red-800 to "active", which is identical to "revoked". Active status typically represents a positive state and should use a different color (e.g., blue).
const InvitationStatuses = {
pending: { label: "Pending", color: "bg-yellow-100 text-yellow-800" },
accepted: { label: "Accepted", color: "bg-green-100 text-green-800" },
revoked: { label: "Revoked", color: "bg-red-100 text-red-800" },
- active: { label: "Active", color: "bg-red-100 text-red-800" },
+ active: { label: "Active", color: "bg-blue-100 text-blue-800" },
} as const📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const InvitationStatuses = { | |
| pending: { label: "Pending", color: "bg-yellow-100 text-yellow-800" }, | |
| accepted: { label: "Accepted", color: "bg-green-100 text-green-800" }, | |
| revoked: { label: "Revoked", color: "bg-red-100 text-red-800" }, | |
| active: { label: "Active", color: "bg-red-100 text-red-800" }, | |
| } as const | |
| const InvitationStatuses = { | |
| pending: { label: "Pending", color: "bg-yellow-100 text-yellow-800" }, | |
| accepted: { label: "Accepted", color: "bg-green-100 text-green-800" }, | |
| revoked: { label: "Revoked", color: "bg-red-100 text-red-800" }, | |
| active: { label: "Active", color: "bg-blue-100 text-blue-800" }, | |
| } as const |
🤖 Prompt for AI Agents
In apps/admin-panel/app/invitations/[id]/page.tsx around lines 18 to 23, the
"active" status is using the same red color as "revoked"; update the "active"
entry to a positive blue variant (for example "bg-blue-100 text-blue-800") so
its styling differs and correctly conveys a positive/active state, preserving
the existing object shape and const assertion.
| <div className="grid gap-6 lg:grid-cols-3"> | ||
| <ClientInfoCard invitation={invitation} /> | ||
|
|
||
| <StatusHistoryCard | ||
| events={events} | ||
| getActor={getActor} | ||
| formatDate={formatDate} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grid column count doesn't match children count.
lg:grid-cols-3 creates a 3-column layout, but only 2 children are rendered (ClientInfoCard and StatusHistoryCard). This leaves an empty column. Consider using lg:grid-cols-2 or verify if a third card is intended.
- <div className="grid gap-6 lg:grid-cols-3">
+ <div className="grid gap-6 lg:grid-cols-2">
<ClientInfoCard invitation={invitation} />
<StatusHistoryCard📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="grid gap-6 lg:grid-cols-3"> | |
| <ClientInfoCard invitation={invitation} /> | |
| <StatusHistoryCard | |
| events={events} | |
| getActor={getActor} | |
| formatDate={formatDate} | |
| /> | |
| </div> | |
| <div className="grid gap-6 lg:grid-cols-2"> | |
| <ClientInfoCard invitation={invitation} /> | |
| <StatusHistoryCard | |
| events={events} | |
| getActor={getActor} | |
| formatDate={formatDate} | |
| /> | |
| </div> |
🤖 Prompt for AI Agents
In apps/admin-panel/app/invitations/[id]/page.tsx around lines 204–212 the grid
is declared with lg:grid-cols-3 but only two child components are rendered,
leaving an empty column; change the grid class to lg:grid-cols-2 (or add the
intended third card) so the column count matches the number of children — update
the className on the wrapping div to use lg:grid-cols-2 unless you actually
intend to render a third component, in which case add that component in the same
div.
| setSendPush(found.sendPush ?? true) | ||
| setAddToHistory(found.addToHistory ?? true) | ||
| setAction((found as any).action ?? "Open Deep Link") | ||
| setDeepLinkScreen((found as any).deepLinkScreen ?? "Wallet") | ||
| setDeepLinkAction((found as any).deepLinkAction ?? "None") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Avoid as any casts; extend TemplateRow type instead.
The code accesses properties (action, deepLinkScreen, deepLinkAction) that don't exist on TemplateRow. Instead of using as any, consider extending the type definition to include these optional properties.
In apps/admin-panel/app/invitations/types.ts, extend TemplateRow:
export type TemplateRow = {
id: string
name: string
language: string
icon: TemplateIcon
title: string
body: string
sendPush?: boolean
addToHistory?: boolean
action?: string
deepLinkScreen?: string
deepLinkAction?: string
}Then update the code:
- setAction((found as any).action ?? "Open Deep Link")
- setDeepLinkScreen((found as any).deepLinkScreen ?? "Wallet")
- setDeepLinkAction((found as any).deepLinkAction ?? "None")
+ setAction(found.action ?? "Open Deep Link")
+ setDeepLinkScreen(found.deepLinkScreen ?? "Wallet")
+ setDeepLinkAction(found.deepLinkAction ?? "None")🤖 Prompt for AI Agents
In apps/admin-panel/app/templates/[id]/edit/page.tsx around lines 32-36, the
code uses `as any` to access action, deepLinkScreen, and deepLinkAction which
are missing from TemplateRow; extend the TemplateRow type in
apps/admin-panel/app/invitations/types.ts to add optional fields action?:
string, deepLinkScreen?: string, deepLinkAction?: string (keep sendPush? and
addToHistory? if not present), then update imports so the component uses the
typed TemplateRow and remove the `as any` casts, keeping the existing nullish
defaults (e.g. ?? "Open Deep Link") when setting state.
| <label className="flex items-center gap-2"> | ||
| <input | ||
| type="checkbox" | ||
| checked={false} | ||
| onChange={() => {}} | ||
| className="h-4 w-4 rounded" | ||
| /> | ||
| <span className="text-sm text-gray-700">Add to Bulletin</span> | ||
| </label> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Add to Bulletin" checkbox is non-functional.
Similar to the Icon select, this checkbox has checked={false} and an empty onChange, making it non-interactive. Either implement the state management or remove the control until it's needed.
🤖 Prompt for AI Agents
In apps/admin-panel/app/templates/[id]/edit/page.tsx around lines 155-163, the
"Add to Bulletin" checkbox is hard-coded with checked={false} and an empty
onChange, so it is non-interactive; fix by wiring it to component state (e.g.,
create a useState boolean, use that state for checked, and update it in
onChange) or remove the input until it's needed; if you keep it, also ensure the
bulletin flag is included in the form submission/update handler so the backend
receives the value.
| <select | ||
| className="mt-1 block w-full rounded-md border border-gray-200 px-3 py-2" | ||
| value={template.icon} | ||
| onChange={() => {}} | ||
| > | ||
| <option value="star">★ Star</option> | ||
| <option value="check">✓ Check</option> | ||
| <option value="bell">🔔 Bell</option> | ||
| </select> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Icon select is non-functional.
The Icon select displays template.icon but the onChange handler is empty, so users cannot change the icon. Either wire up state management or mark the field as read-only.
+ const [icon, setIcon] = useState(template?.icon || "star")
+
+ // In useEffect where template is loaded:
+ setIcon(found.icon)
+
+ // In the select:
<select
className="mt-1 block w-full rounded-md border border-gray-200 px-3 py-2"
- value={template.icon}
- onChange={() => {}}
+ value={icon}
+ onChange={(e) => setIcon(e.target.value)}
>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/admin-panel/app/templates/[id]/edit/page.tsx around lines 184 to 193 the
<select> is controlled by template.icon but the onChange is a no-op, making the
icon non-functional; update the handler to either set the new icon into
component state or props (e.g., read the event.target.value and call the
existing setTemplate/updateTemplate function to set template.icon) or explicitly
mark the field readOnly/disabled if it must not be editable; ensure you handle
the event type correctly and preserve other template fields when updating.
| <td className="px-6 py-4 text-gray-700"> | ||
| {template.icon === TemplateIcon.Star ? ( | ||
| <span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-yellow-500"> | ||
| ★ | ||
| </span> | ||
| ) : ( | ||
| <span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-green-600"> | ||
| ✓ | ||
| </span> | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing handling for "bell" icon type.
The icon rendering only handles "star" explicitly; all other icons default to the checkmark. Add explicit handling for the "bell" icon defined in TemplateIcon.
<td className="px-6 py-4 text-gray-700">
{template.icon === TemplateIcon.Star ? (
<span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-yellow-500">
★
</span>
+ ) : template.icon === TemplateIcon.Bell ? (
+ <span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-blue-500">
+ 🔔
+ </span>
) : (
<span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-green-600">
✓
</span>
)}
</td>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <td className="px-6 py-4 text-gray-700"> | |
| {template.icon === TemplateIcon.Star ? ( | |
| <span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-yellow-500"> | |
| ★ | |
| </span> | |
| ) : ( | |
| <span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-green-600"> | |
| ✓ | |
| </span> | |
| )} | |
| <td className="px-6 py-4 text-gray-700"> | |
| {template.icon === TemplateIcon.Star ? ( | |
| <span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-yellow-500"> | |
| ★ | |
| </span> | |
| ) : template.icon === TemplateIcon.Bell ? ( | |
| <span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-blue-500"> | |
| 🔔 | |
| </span> | |
| ) : ( | |
| <span className="inline-flex h-6 w-6 items-center justify-center rounded-full border border-gray-300 text-green-600"> | |
| ✓ | |
| </span> | |
| )} | |
| </td> |
🤖 Prompt for AI Agents
In apps/admin-panel/app/templates/page.tsx around lines 71 to 80, the JSX only
checks for TemplateIcon.Star and otherwise renders a checkmark, so
TemplateIcon.Bell is not handled; add an explicit conditional branch for
TemplateIcon.Bell (rendering the bell glyph/icon with its own styling and
classes) between the Star and default branches so the bell icon displays
correctly, and keep the existing checkmark as the fallback for any other/unknown
icon values.
| <input | ||
| placeholder="e.g., Weekly Digest" | ||
| className="mt-1 block w-full rounded-md border border-gray-200 px-3 py-2" | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create modal form inputs are uncontrolled.
The Template Name, Title, and Body inputs have no value or onChange handlers, so the entered data cannot be captured when "Save Template" is clicked. Add state management for these fields.
+ const [createName, setCreateName] = useState("")
+ const [createTitle, setCreateTitle] = useState("")
+ const [createBody, setCreateBody] = useState("")
// Then in the JSX:
<input
placeholder="e.g., Weekly Digest"
+ value={createName}
+ onChange={(e) => setCreateName(e.target.value)}
className="mt-1 block w-full rounded-md border border-gray-200 px-3 py-2"
/>Also applies to: 230-244
🤖 Prompt for AI Agents
In apps/admin-panel/app/templates/page.tsx around lines 136-139 and 230-244, the
modal form inputs for Template Name, Title, and Body are uncontrolled and their
values are not captured on submit; add React state (useState) for each field,
set each input/textarea's value to the corresponding state variable and wire an
onChange handler to update state, and then read those state variables in the
"Save Template" handler (or pass them to the existing save function). Initialize
state to empty strings (or sensible defaults), ensure types if using TypeScript,
and if the modal is a child component either lift state up or expose
callbacks/props so the parent can receive the final values on save.
| onAddHistoryChange, | ||
| onSave, | ||
| }: ChangeStatusModalProps) { | ||
| const [addToBulletin, setAddToBulletin] = useState(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addToBulletin state is not communicated to parent on save.
The addToBulletin checkbox updates local state but this value is never passed back to the parent component via onSave or a callback. Either add an onAddToBulletinChange prop similar to the other toggle callbacks, or include addToBulletin in an extended onSave signature.
Option 1 - Add callback prop:
interface ChangeStatusModalProps {
// ...existing props...
onAddHistoryChange: (checked: boolean) => void
+ onAddToBulletinChange: (checked: boolean) => void
onSave: () => void
}Option 2 - Include in save:
- onSave: () => void
+ onSave: (options: { addToBulletin: boolean }) => void
- onClick={onSave}
+ onClick={() => onSave({ addToBulletin })}Also applies to: 122-130
🤖 Prompt for AI Agents
In apps/admin-panel/components/invitations/change-status-modal.tsx around lines
33 and 122-130, the local state addToBulletin is toggled but never communicated
to the parent when saving; update the component API and save flow to surface
this value. Either add a new prop onAddToBulletinChange: (value: boolean) =>
void and call it whenever addToBulletin changes, or extend the existing onSave
signature to accept an options object (e.g., onSave({ status, addToBulletin }))
and pass addToBulletin in the save handler; update the parent call sites to
handle the new callback/argument and bump prop types accordingly.
| <td className="py-2 text-gray-900">{invitation.id}</td> | ||
| </tr> | ||
| <tr> | ||
| <td className="py-2 font-medium tex-gray-700">LN Address</td> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in className: tex-gray-700 should be text-gray-700.
The missing "t" will prevent the text color styling from being applied to the "LN Address" label.
- <td className="py-2 font-medium tex-gray-700">LN Address</td>
+ <td className="py-2 font-medium text-gray-700">LN Address</td>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <td className="py-2 font-medium tex-gray-700">LN Address</td> | |
| <td className="py-2 font-medium text-gray-700">LN Address</td> |
🤖 Prompt for AI Agents
In apps/admin-panel/components/invitations/client-info-card.tsx around line 19,
the className on the "LN Address" table cell has a typo "tex-gray-700"
preventing the text color style from applying; change it to "text-gray-700" so
Tailwind applies the intended color (i.e., update the className string to
include the missing "t").
| events | ||
| .sort( | ||
| (a, b) => | ||
| new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(), | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid mutating the events prop array.
Array.sort() mutates the array in place. Since events is a prop, mutating it can cause unexpected side effects in the parent component.
{events.length > 0 ? (
- events
- .sort(
+ [...events]
+ .sort(
(a, b) =>
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| events | |
| .sort( | |
| (a, b) => | |
| new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(), | |
| ) | |
| [...events] | |
| .sort( | |
| (a, b) => | |
| new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(), | |
| ) |
🤖 Prompt for AI Agents
In apps/admin-panel/components/invitations/status-history-card.tsx around lines
28 to 32, the code calls events.sort(...) which mutates the events prop; make a
non-mutating copy before sorting (e.g., use [...events] or events.slice()) and
sort that copy instead, or compute the sorted array in a useMemo to avoid
re-sorting on every render.
littledino2112
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The PR just has UI code, there's no integration with the backend yet (i.e. doesn't have any call to notifications service to send out invitations)
- There's added function for 'status' tracking for invitation but unclear to me how this will be implemented. For example, you have a 'revoked' status. Afaik, it's not possible to revoke a push notification that was already sent.
Can you elaborate on what this status tracking is? What kind of backend api you'll need for this?
PR for Backend: |
There was a problem hiding this 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
♻️ Duplicate comments (1)
apps/admin-panel/app/mock-data.ts (1)
128-137: Pipeline failure: Add German words to typos allowlist.The spell checker is flagging valid German words
"ist"and"Sie"as typos. Add these words totypos.tomlto fix the pipeline failure:[default.extend-words] ist = "ist" Sie = "Sie"
🧹 Nitpick comments (3)
apps/admin-panel/app/mock-data.ts (1)
5-28: Remove unused import and consider extendingNotificationContent.
NotificationContentis imported but never used. Additionally,InvitationContentduplicates nearly all fields fromNotificationContent, only addingid. Consider:import { InvitationRow, TemplateRow } from "./invitations/types" import { NotificationContent, LocalizedNotificationContent, } from "../components/notification/builder" -import { NotificationIcon, DeepLinkScreen, DeepLinkAction } from "../generated" -import { NotificationAction } from "../components/notification/types" -export type InvitationContent = { - id: string - localizedNotificationContents: LocalizedNotificationContent[] - action?: NotificationAction - openDeepLink?: { - screen?: DeepLinkScreen | undefined - action?: DeepLinkAction | undefined - } - openExternalUrl?: { - url: string - } - icon?: NotificationIcon | undefined - shouldSendPush: boolean - shouldAddToHistory: boolean - shouldAddToBulletin: boolean -} +export type InvitationContent = NotificationContent & { id: string }Given this is temporary mock data, this is optional.
apps/admin-panel/app/invitations/new/page.tsx (2)
83-122: Fix function naming and event type signature.
InvitationSenderuses PascalCase (reserved for components/classes). Use camelCase:handleSendInvitationorsendInvitation.The function expects
React.FormEventbut is called fromButton.onClickwhich passesReact.MouseEvent. While both havepreventDefault(), the types don't match.- const InvitationSender = async (e: React.FormEvent) => { + const handleSendInvitation = async (e: React.MouseEvent<HTMLButtonElement>) => { e.preventDefault() // ... }And update the button:
- <Button onClick={InvitationSender} disabled={!canSend}> + <Button onClick={handleSendInvitation} disabled={!canSend}>
235-243: Consider post-success UX: reset form or redirect.After a successful send, the success message displays but the form retains its values. Users sending multiple invitations would need to manually clear the form. Consider:
- Resetting form state after success, or
- Redirecting to the invitations list, or
- Adding a "Send Another" button
if (res.success) { setSuccess(true) // Option 1: Reset form setUserQuery("") setTemplateId("") // Option 2: Redirect after delay setTimeout(() => router.push("/invitations"), 1500) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/admin-panel/app/invitations/new/page.tsx(1 hunks)apps/admin-panel/app/invitations/types.ts(1 hunks)apps/admin-panel/app/mock-data.ts(1 hunks)apps/admin-panel/components/notification/builder.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/admin-panel/app/invitations/types.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/admin-panel/app/mock-data.ts (2)
apps/admin-panel/components/notification/builder.tsx (1)
LocalizedNotificationContent(339-343)apps/admin-panel/app/invitations/types.ts (2)
InvitationRow(3-8)TemplateRow(29-39)
🪛 GitHub Actions: Spelling
apps/admin-panel/app/mock-data.ts
[warning] 132-132: ist should be is, it, its, sit, or list (typo rule)
[warning] 133-133: Sie should be Size, Sigh, or Side (typo rule)
[warning] 208-208: ist should be is, it, its, sit, or list (typo rule)
[warning] 209-209: Sie should be Size, Sigh, or Side (typo rule)
[error] 132-132: ist should be is, it, its, sit, or list
[error] 133-133: Sie should be Size, Sigh, or Side
[error] 208-208: ist should be is, it, its, sit, or list
[error] 209-209: Sie should be Size, Sigh, or Side
🪛 GitHub Check: Spell Check with Typos
apps/admin-panel/app/mock-data.ts
[warning] 209-209:
"Sie" should be "Size" or "Sigh" or "Side".
[warning] 208-208:
"ist" should be "is" or "it" or "its" or "sit" or "list".
[warning] 133-133:
"Sie" should be "Size" or "Sigh" or "Side".
[warning] 132-132:
"ist" should be "is" or "it" or "its" or "sit" or "list".
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: build and test admin-panel
- GitHub Check: execute via buck2
- GitHub Check: execute via bats
- GitHub Check: Check SDLs
- GitHub Check: Migrate Mongodb
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
apps/admin-panel/components/notification/builder.tsx (1)
339-343: LGTM!Exporting
LocalizedNotificationContentis appropriate as it enables reuse in other modules like the mock data file.apps/admin-panel/app/mock-data.ts (2)
30-91: LGTM!Mock invitation data provides good coverage of different statuses for development testing.
161-164: String literals are type-safe in this context and do not require refactoring.The enums are defined as const objects with string literal values (
DeepLinkScreen = { Chat: 'CHAT', ... }andNotificationIcon = { Bell: 'BELL', ... }). The TypeScript type system treats this as a union of string literal types, which meansscreen: "CHAT"andicon: "BELL"are already validated at compile time. Using string literals directly provides the same type safety as referencing the enum members (e.g.,DeepLinkScreen.Chat), and both approaches are valid. If the underlying enum values change, TypeScript will surface errors either way.apps/admin-panel/app/invitations/new/page.tsx (1)
40-58: Only the first localization is preserved when modifying templates.The
invitationTemplateconstruction only keepslocalizedNotificationContents[0], discarding any additional localizations from the selected template. If templates support multiple languages, this would result in data loss.If single-language invitations are intentional, consider adding a comment. Otherwise, consider preserving all localizations while allowing edits to the displayed one.
There was a problem hiding this 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 (3)
apps/admin-panel/app/invitations/new/page.tsx (3)
93-93: Consider using camelCase for function names.The function name
InvitationSenderuses PascalCase, which is typically reserved for React component names. For consistency with JavaScript/TypeScript conventions, consider renaming to camelCase (e.g.,handleInvitationSubmitorsendInvitation).- const InvitationSender = async (e: React.FormEvent) => { + const handleInvitationSubmit = async (e: React.FormEvent) => {Don't forget to update the reference at line 277:
- <Button onClick={InvitationSender} disabled={!canSend}> + <Button onClick={handleInvitationSubmit} disabled={!canSend}>
93-94: Wrap form fields in a<form>element for better semantics.The handler is typed as
React.FormEventbut is called from a Button'sonClick(which produces aMouseEvent). WhilepreventDefault()works on both event types, wrapping the form fields in a<form>element and usingonSubmitprovides better semantic HTML, native form validation support, and proper keyboard accessibility (e.g., Enter key submission).Example refactor:
- <div className="mx-auto max-w-2xl space-y-6"> + <form onSubmit={handleInvitationSubmit} className="mx-auto max-w-2xl space-y-6"> <div className="rounded-lg border border-gray-200 bg-white p-6"> {/* form fields */} </div> <div className="flex justify-start"> - <Button onClick={InvitationSender} disabled={!canSend}> + <Button type="submit" disabled={!canSend}> <span className="mr-2">➤</span> <span>Send Invite</span> </Button> </div> {/* feedback messages */} - </div> + </form>Also applies to: 159-281
163-169: Addaria-labelto the close button for accessibility.The close button lacks a text label, making it unclear to screen reader users. Adding an
aria-labelimproves accessibility.<button type="button" onClick={() => router.back()} className="inline-flex h-9 w-9 items-center justify-center rounded-full border border-gray-300 text-gray-500 hover:bg-gray-50" + aria-label="Close" > ✕ </button>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/admin-panel/app/invitations/new/page.tsx(1 hunks)apps/admin-panel/app/invitations/types.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/admin-panel/app/invitations/types.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/admin-panel/app/invitations/new/page.tsx (4)
apps/admin-panel/app/invitations/types.ts (2)
FormState(54-62)SubmitState(64-68)apps/admin-panel/app/mock-data.ts (1)
notificationContentMock(151-237)apps/admin-panel/components/notification/notification-actions.tsx (1)
userIdByUsername(127-157)apps/admin-panel/components/shared/form-controls/form-controls.tsx (4)
TextInput(11-14)SelectInput(16-23)TextArea(25-28)Checkbox(30-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: build and test admin-panel
- GitHub Check: execute via bats
- GitHub Check: Migrate Mongodb
- GitHub Check: Check SDLs
- GitHub Check: execute via buck2
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
apps/admin-panel/app/invitations/new/page.tsx (2)
41-91: Well-structured computed values and synchronization logic.The memoized values (
selectedTemplate,invitationTemplate,canSend) are efficiently implemented with correct dependencies. TheuseEffectproperly synchronizes the selected template's content and flags into the form state, clearing fields when no template is selected. This approach keeps the UI responsive and prevents unnecessary recalculations.
100-153: Comprehensive error handling and validation.The submission flow properly handles multiple failure scenarios:
- User not found or invalid username
- Missing template selection
- API call failures
- Unexpected errors
Each path correctly updates the submit state and provides actionable error messages to the user.
* refactor(core): make network fee mandatory * fix: intraledger withdrawal fee calc test * fix: cmd substitution error handling
…3e68dc0b9c9577214154d4a7db5d70aa5dcfc4a0de193af9184f4
…linkbitcoin#350) * fix(core): use transaction fee when sender is exempt from bank fees * test: add test to cover bank fee exemption * fix: use ZERO_SATS const
…4380cb6435ee68d7bf0967fe4239c0163c1d011dd5d7c0f861f53
…o feat--visa-card-invitations
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.