From e2560b089b100bc8dbd718fd275027bf2a569aaf Mon Sep 17 00:00:00 2001 From: Oliver Bryan <04oliverbryan@gmail.com> Date: Sat, 17 Jan 2026 22:12:29 +0000 Subject: [PATCH] use Icon component --- packages/frontend/src/components/avatar.tsx | 4 +- .../src/components/issue-detail-pane.tsx | 8 ++-- .../src/components/log-out-button.tsx | 4 +- .../frontend/src/components/login-form.tsx | 6 +-- .../src/components/multi-assignee-select.tsx | 4 +- .../src/components/organisations-dialog.tsx | 42 +++++++++++++------ .../server-configuration-dialog.tsx | 8 ++-- .../frontend/src/components/theme-toggle.tsx | 4 +- .../frontend/src/components/timer-modal.tsx | 4 +- .../frontend/src/components/ui/calendar.tsx | 10 +++-- .../frontend/src/components/ui/dialog.tsx | 4 +- .../src/components/ui/dropdown-menu.tsx | 8 ++-- packages/frontend/src/components/ui/icon.tsx | 16 ++++--- packages/frontend/src/components/ui/input.tsx | 4 +- .../frontend/src/components/ui/resizable.tsx | 4 +- .../frontend/src/components/ui/select.tsx | 13 +++--- .../frontend/src/components/ui/sonner.tsx | 12 +++--- .../frontend/src/components/ui/spinner.tsx | 6 +-- .../frontend/src/components/upload-avatar.tsx | 4 +- packages/frontend/src/pages/NotFound.tsx | 4 +- todo.md | 2 - 21 files changed, 97 insertions(+), 74 deletions(-) diff --git a/packages/frontend/src/components/avatar.tsx b/packages/frontend/src/components/avatar.tsx index 67f154e..5d4830d 100644 --- a/packages/frontend/src/components/avatar.tsx +++ b/packages/frontend/src/components/avatar.tsx @@ -1,5 +1,5 @@ -import { UserRound } from "lucide-react"; import { useSession } from "@/components/session-provider"; +import Icon from "@/components/ui/icon"; import { cn } from "@/lib/utils"; const FALLBACK_COLOURS = [ @@ -89,7 +89,7 @@ export default function Avatar({ ) : name ? ( {getInitials(name)} ) : ( - + )} ); diff --git a/packages/frontend/src/components/issue-detail-pane.tsx b/packages/frontend/src/components/issue-detail-pane.tsx index 7f06fa9..1622275 100644 --- a/packages/frontend/src/components/issue-detail-pane.tsx +++ b/packages/frontend/src/components/issue-detail-pane.tsx @@ -1,5 +1,4 @@ import type { IssueResponse, ProjectResponse, SprintRecord, UserRecord } from "@sprint/shared"; -import { Check, Link, Trash, X } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { toast } from "sonner"; import { MultiAssigneeSelect } from "@/components/multi-assignee-select"; @@ -10,6 +9,7 @@ import StatusTag from "@/components/status-tag"; import { TimerDisplay } from "@/components/timer-display"; import { TimerModal } from "@/components/timer-modal"; import { ConfirmDialog } from "@/components/ui/confirm-dialog"; +import Icon from "@/components/ui/icon"; import { SelectTrigger } from "@/components/ui/select"; import { issue } from "@/lib/server"; import { issueID } from "@/lib/utils"; @@ -246,13 +246,13 @@ export function IssueDetailPane({
- {linkCopied ? : } + {linkCopied ? : } - + - +
diff --git a/packages/frontend/src/components/log-out-button.tsx b/packages/frontend/src/components/log-out-button.tsx index e7b3f9b..7176726 100644 --- a/packages/frontend/src/components/log-out-button.tsx +++ b/packages/frontend/src/components/log-out-button.tsx @@ -1,6 +1,6 @@ -import { LogOut } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { Button } from "@/components/ui/button"; +import Icon from "@/components/ui/icon"; import { clearAuth, cn, getCsrfToken, getServerURL } from "@/lib/utils"; export default function LogOutButton({ @@ -37,7 +37,7 @@ export default function LogOutButton({ size={noStyle ? "none" : "default"} > Log out - + ); } diff --git a/packages/frontend/src/components/login-form.tsx b/packages/frontend/src/components/login-form.tsx index dad2608..842c01f 100644 --- a/packages/frontend/src/components/login-form.tsx +++ b/packages/frontend/src/components/login-form.tsx @@ -1,7 +1,6 @@ /** biome-ignore-all lint/correctness/useExhaustiveDependencies: <> */ import { USER_NAME_MAX_LENGTH, USER_USERNAME_MAX_LENGTH } from "@sprint/shared"; -import { AlertTriangle, X } from "lucide-react"; import { useEffect, useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import Avatar from "@/components/avatar"; @@ -10,6 +9,7 @@ import { useSession } from "@/components/session-provider"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Field } from "@/components/ui/field"; +import Icon from "@/components/ui/icon"; import { IconButton } from "@/components/ui/icon-button"; import { Label } from "@/components/ui/label"; import { UploadAvatar } from "@/components/upload-avatar"; @@ -153,9 +153,9 @@ export default function LogInForm() { setShowWarning(false); }} > - + - +

This application is currently under construction. Your data is very likely to be diff --git a/packages/frontend/src/components/multi-assignee-select.tsx b/packages/frontend/src/components/multi-assignee-select.tsx index e4ceff2..9a143b2 100644 --- a/packages/frontend/src/components/multi-assignee-select.tsx +++ b/packages/frontend/src/components/multi-assignee-select.tsx @@ -1,5 +1,5 @@ import type { UserRecord } from "@sprint/shared"; -import { Plus } from "lucide-react"; +import Icon from "@/components/ui/icon"; import { IconButton } from "@/components/ui/icon-button"; import { UserSelect } from "@/components/user-select"; @@ -62,7 +62,7 @@ export function MultiAssigneeSelect({

{index === assigneeIds.length - 1 && canAddMore && ( - + )} diff --git a/packages/frontend/src/components/organisations-dialog.tsx b/packages/frontend/src/components/organisations-dialog.tsx index 80d107d..a0d8ac3 100644 --- a/packages/frontend/src/components/organisations-dialog.tsx +++ b/packages/frontend/src/components/organisations-dialog.tsx @@ -7,7 +7,6 @@ import { type ProjectResponse, type SprintRecord, } from "@sprint/shared"; -import { ChevronDown, ChevronUp, EllipsisVertical, Plus, X } from "lucide-react"; import { type ReactNode, useCallback, useEffect, useState } from "react"; import { toast } from "sonner"; import { AddMemberDialog } from "@/components/add-member-dialog"; @@ -28,6 +27,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import Icon from "@/components/ui/icon"; import { IconButton } from "@/components/ui/icon-button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -571,9 +571,15 @@ function OrganisationsDialog({ > {member.OrganisationMember.role === "admin" ? ( - + ) : ( - + )} - + )} @@ -609,7 +615,7 @@ function OrganisationsDialog({ }} trigger={ } /> @@ -683,7 +689,10 @@ function OrganisationsDialog({ trigger={ } sprints={sprints} @@ -726,7 +735,10 @@ function OrganisationsDialog({ noStyle className="hover:opacity-80 cursor-pointer" > - + - + Move up - + Move down - + Remove @@ -815,7 +833,7 @@ function OrganisationsDialog({ ISSUE_STATUS_MAX_LENGTH } > - + {statusError && ( @@ -833,7 +851,7 @@ function OrganisationsDialog({ }} className="flex gap-2 w-full min-w-0" > - Create status + Create status ))} diff --git a/packages/frontend/src/components/server-configuration-dialog.tsx b/packages/frontend/src/components/server-configuration-dialog.tsx index b5f1f30..5db0c3e 100644 --- a/packages/frontend/src/components/server-configuration-dialog.tsx +++ b/packages/frontend/src/components/server-configuration-dialog.tsx @@ -1,7 +1,7 @@ -import { CheckIcon, ServerIcon, Undo2 } from "lucide-react"; import { type ReactNode, useState } from "react"; import { createPortal } from "react-dom"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import Icon from "@/components/ui/icon"; import { IconButton } from "@/components/ui/icon-button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -118,7 +118,7 @@ export function ServerConfigurationDialog({ trigger }: { trigger?: ReactNode }) className="absolute top-2 right-2" title={"Server Configuration"} > - + )} @@ -152,7 +152,7 @@ export function ServerConfigurationDialog({ trigger }: { trigger?: ReactNode }) disabled={!canSave || isCheckingHealth} onClick={handleSave} > - + - + {!isValid && ( diff --git a/packages/frontend/src/components/theme-toggle.tsx b/packages/frontend/src/components/theme-toggle.tsx index b2b7417..9847db7 100644 --- a/packages/frontend/src/components/theme-toggle.tsx +++ b/packages/frontend/src/components/theme-toggle.tsx @@ -1,5 +1,5 @@ -import { Moon, Sun } from "lucide-react"; import { useTheme } from "@/components/theme-provider"; +import Icon from "@/components/ui/icon"; import { IconButton } from "@/components/ui/icon-button"; import { cn } from "@/lib/utils"; @@ -20,7 +20,7 @@ function ThemeToggle({ className }: { className?: string }) { onClick={() => setTheme(isDark ? "light" : "dark")} title={isDark ? "Switch to light mode" : "Switch to dark mode"} > - {isDark ? : } + {isDark ? : } ); } diff --git a/packages/frontend/src/components/timer-modal.tsx b/packages/frontend/src/components/timer-modal.tsx index 31f161f..b534e47 100644 --- a/packages/frontend/src/components/timer-modal.tsx +++ b/packages/frontend/src/components/timer-modal.tsx @@ -1,8 +1,8 @@ -import { Timer } from "lucide-react"; import { useState } from "react"; import { IssueTimer } from "@/components/issue-timer"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; +import Icon from "@/components/ui/icon"; export function TimerModal({ issueId }: { issueId: number }) { const [open, setOpen] = useState(false); @@ -11,7 +11,7 @@ export function TimerModal({ issueId }: { issueId: number }) { diff --git a/packages/frontend/src/components/ui/calendar.tsx b/packages/frontend/src/components/ui/calendar.tsx index 1390bcc..e087be6 100644 --- a/packages/frontend/src/components/ui/calendar.tsx +++ b/packages/frontend/src/components/ui/calendar.tsx @@ -1,8 +1,8 @@ import type { SprintRecord } from "@sprint/shared"; -import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; import * as React from "react"; import { type DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"; import { Button, buttonVariants } from "@/components/ui/button"; +import Icon from "@/components/ui/icon"; import { cn } from "@/lib/utils"; function Calendar({ @@ -109,14 +109,16 @@ function Calendar({ }, Chevron: ({ className, orientation, ...props }) => { if (orientation === "left") { - return ; + return ; } if (orientation === "right") { - return ; + return ( + + ); } - return ; + return ; }, DayButton: (props) => , WeekNumber: ({ children, ...props }) => { diff --git a/packages/frontend/src/components/ui/dialog.tsx b/packages/frontend/src/components/ui/dialog.tsx index d1ec38d..a4ee1e3 100644 --- a/packages/frontend/src/components/ui/dialog.tsx +++ b/packages/frontend/src/components/ui/dialog.tsx @@ -1,6 +1,6 @@ import * as DialogPrimitive from "@radix-ui/react-dialog"; -import { XIcon } from "lucide-react"; import type * as React from "react"; +import Icon from "@/components/ui/icon"; import { cn } from "@/lib/utils"; @@ -71,7 +71,7 @@ function DialogContent({ "[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", )} > - + Close )} diff --git a/packages/frontend/src/components/ui/dropdown-menu.tsx b/packages/frontend/src/components/ui/dropdown-menu.tsx index a63aff1..24424bf 100644 --- a/packages/frontend/src/components/ui/dropdown-menu.tsx +++ b/packages/frontend/src/components/ui/dropdown-menu.tsx @@ -1,6 +1,6 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; -import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"; import type * as React from "react"; +import Icon from "@/components/ui/icon"; import { cn } from "@/lib/utils"; function DropdownMenu({ ...props }: React.ComponentProps) { @@ -136,7 +136,7 @@ function DropdownMenuCheckboxItem({ > - + {children} @@ -168,7 +168,7 @@ function DropdownMenuRadioItem({ > - + {children} @@ -242,7 +242,7 @@ function DropdownMenuSubTrigger({ {...props} > {children} - + ); } diff --git a/packages/frontend/src/components/ui/icon.tsx b/packages/frontend/src/components/ui/icon.tsx index 290d460..54678e6 100644 --- a/packages/frontend/src/components/ui/icon.tsx +++ b/packages/frontend/src/components/ui/icon.tsx @@ -56,6 +56,7 @@ import { WarningIcon as PhosphorWarning, XIcon as PhosphorX, } from "@phosphor-icons/react"; +import type { IconStyle } from "@sprint/shared"; import { AlertTriangle, Check, @@ -92,6 +93,7 @@ import { UserRound, X, } from "lucide-react"; +import { useSessionSafe } from "@/components/session-provider"; const icons = { alertTriangle: { lucide: AlertTriangle, pixel: PixelAlert, phosphor: PhosphorWarning }, @@ -141,11 +143,11 @@ const icons = { export type IconName = keyof typeof icons; export const iconNames = Object.keys(icons) as IconName[]; export const iconStyles = ["lucide", "pixel", "phosphor"] as const; -export type IconStyle = (typeof iconStyles)[number]; +export type { IconStyle }; export default function Icon({ icon, - iconStyle = "lucide", + iconStyle, size = 24, ...props }: { @@ -154,7 +156,9 @@ export default function Icon({ size?: number | string; color?: string; } & React.ComponentProps<"svg">) { - const IconComponent = icons[icon]?.[iconStyle]; + const session = useSessionSafe(); + const resolvedStyle = (iconStyle ?? session?.user?.iconPreference ?? "lucide") as IconStyle; + const IconComponent = icons[icon]?.[resolvedStyle]; if (!IconComponent) { return null; @@ -164,9 +168,9 @@ export default function Icon({ {showHashPrefix && ( - + )} {withHandle && (
- +
)} diff --git a/packages/frontend/src/components/ui/select.tsx b/packages/frontend/src/components/ui/select.tsx index 9a5fdc9..fc7badd 100644 --- a/packages/frontend/src/components/ui/select.tsx +++ b/packages/frontend/src/components/ui/select.tsx @@ -1,6 +1,6 @@ import * as SelectPrimitive from "@radix-ui/react-select"; -import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"; import type * as React from "react"; +import Icon from "@/components/ui/icon"; import { cn } from "@/lib/utils"; @@ -72,8 +72,9 @@ function SelectTrigger({ )} {children} - @@ -165,7 +166,7 @@ function SelectItem({ className="absolute right-2 flex size-3.5 items-center justify-center" > - + {textClassName ? ( @@ -197,7 +198,7 @@ function SelectScrollUpButton({ className={cn("flex cursor-default items-center justify-center py-1", className)} {...props} > - + ); } @@ -212,7 +213,7 @@ function SelectScrollDownButton({ className={cn("flex cursor-default items-center justify-center py-1", className)} {...props} > - + ); } diff --git a/packages/frontend/src/components/ui/sonner.tsx b/packages/frontend/src/components/ui/sonner.tsx index 1952ed9..e8cb428 100644 --- a/packages/frontend/src/components/ui/sonner.tsx +++ b/packages/frontend/src/components/ui/sonner.tsx @@ -1,6 +1,6 @@ -import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon } from "lucide-react"; import { useTheme } from "next-themes"; import { Toaster as Sonner, type ToasterProps } from "sonner"; +import Icon from "@/components/ui/icon"; const Toaster = ({ ...props }: ToasterProps) => { const { theme = "system" } = useTheme(); @@ -10,11 +10,11 @@ const Toaster = ({ ...props }: ToasterProps) => { theme={theme as ToasterProps["theme"]} className="toaster group" icons={{ - success: , - info: , - warning: , - error: , - loading: , + success: , + info: , + warning: , + error: , + loading: , }} style={ { diff --git a/packages/frontend/src/components/ui/spinner.tsx b/packages/frontend/src/components/ui/spinner.tsx index 4e98503..68aadf4 100644 --- a/packages/frontend/src/components/ui/spinner.tsx +++ b/packages/frontend/src/components/ui/spinner.tsx @@ -1,10 +1,10 @@ -import { Loader } from "lucide-react"; - +import Icon from "@/components/ui/icon"; import { cn } from "@/lib/utils"; function Spinner({ className, ...props }: React.ComponentProps<"svg">) { return ( - - + )} diff --git a/packages/frontend/src/pages/NotFound.tsx b/packages/frontend/src/pages/NotFound.tsx index 303778f..cfd3604 100644 --- a/packages/frontend/src/pages/NotFound.tsx +++ b/packages/frontend/src/pages/NotFound.tsx @@ -1,9 +1,9 @@ -import { CircleQuestionMark } from "lucide-react"; +import Icon from "@/components/ui/icon"; export default function NotFound() { return (
- + 404 Not Found
diff --git a/todo.md b/todo.md index 6879025..01ec360 100644 --- a/todo.md +++ b/todo.md @@ -1,7 +1,5 @@ # HIGH PRIORITY -- icons - - respect user.iconPreference throughout the app - projects menu - delete project - sprints