Fix Sass deprecations and enable TypeScript checking

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-19 20:15:18 +00:00
parent f85ca45dfa
commit f3bc433297
13 changed files with 82 additions and 531 deletions

View File

@@ -5,11 +5,6 @@ const nextConfig = {
images: {
unoptimized: true,
},
typescript: {
// TypeScript incorrectly flags CSS imports as errors in Next.js
// This is a known issue: https://github.com/vercel/next.js/issues/54282
ignoreBuildErrors: true,
},
experimental: {
optimizePackageImports: ['@radix-ui/react-icons', '@phosphor-icons/react'],
turbopackScopeHoisting: false,

View File

@@ -1,17 +1,18 @@
@import '../styles/abstracts';
@import '../styles/utilities/utilities';
@import './theme.scss';
@use '../styles/abstracts' as *;
@use '../styles/abstracts/variables' as vars;
@use '../styles/utilities/utilities';
@use './theme.scss';
* {
margin: 0;
padding: 0;
box-sizing: border-box;
border-color: $border;
border-color: vars.$border;
}
body {
background: $background;
color: $foreground;
background: vars.$background;
color: vars.$foreground;
font-family: var(--font-inter), 'Inter', sans-serif;
line-height: 1.5;
}

View File

@@ -1,74 +0,0 @@
import { ComponentProps } from "react"
import ChevronLeft from "lucide-react/dist/esm/icons/chevron-left"
import ChevronRight from "lucide-react/dist/esm/icons/chevron-right"
import { DayPicker } from "react-day-picker"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: ComponentProps<typeof DayPicker>) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row gap-2",
month: "flex flex-col gap-4",
caption: "flex justify-center pt-1 relative items-center w-full",
caption_label: "text-sm font-medium",
nav: "flex items-center gap-1",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-x-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md"
),
day: cn(
buttonVariants({ variant: "ghost" }),
"size-8 p-0 font-normal aria-selected:opacity-100"
),
day_range_start:
"day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
day_range_end:
"day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground aria-selected:text-muted-foreground",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
PreviousMonthButton: ({ className, ...props }) => (
<ChevronLeft className={cn("size-4", className)} {...props} />
),
NextMonthButton: ({ className, ...props }) => (
<ChevronRight className={cn("size-4", className)} {...props} />
),
}}
{...props}
/>
)
}
export { Calendar }

View File

@@ -1,177 +0,0 @@
"use client"
import { ComponentProps } from "react"
import { Command as CommandPrimitive } from "cmdk"
import SearchIcon from "lucide-react/dist/esm/icons/search"
import { cn } from "@/lib/utils"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
function Command({
className,
...props
}: ComponentProps<typeof CommandPrimitive>) {
return (
<CommandPrimitive
data-slot="command"
className={cn(
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
className
)}
{...props}
/>
)
}
function CommandDialog({
title = "Command Palette",
description = "Search for a command to run...",
children,
...props
}: ComponentProps<typeof Dialog> & {
title?: string
description?: string
}) {
return (
<Dialog {...props}>
<DialogHeader className="sr-only">
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<DialogContent className="overflow-hidden p-0">
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children}
</Command>
</DialogContent>
</Dialog>
)
}
function CommandInput({
className,
...props
}: ComponentProps<typeof CommandPrimitive.Input>) {
return (
<div
data-slot="command-input-wrapper"
className="flex h-9 items-center gap-2 border-b px-3"
>
<SearchIcon className="size-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
data-slot="command-input"
className={cn(
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
/>
</div>
)
}
function CommandList({
className,
...props
}: ComponentProps<typeof CommandPrimitive.List>) {
return (
<CommandPrimitive.List
data-slot="command-list"
className={cn(
"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
className
)}
{...props}
/>
)
}
function CommandEmpty({
...props
}: ComponentProps<typeof CommandPrimitive.Empty>) {
return (
<CommandPrimitive.Empty
data-slot="command-empty"
className="py-6 text-center text-sm"
{...props}
/>
)
}
function CommandGroup({
className,
...props
}: ComponentProps<typeof CommandPrimitive.Group>) {
return (
<CommandPrimitive.Group
data-slot="command-group"
className={cn(
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
className
)}
{...props}
/>
)
}
function CommandSeparator({
className,
...props
}: ComponentProps<typeof CommandPrimitive.Separator>) {
return (
<CommandPrimitive.Separator
data-slot="command-separator"
className={cn("bg-border -mx-1 h-px", className)}
{...props}
/>
)
}
function CommandItem({
className,
...props
}: ComponentProps<typeof CommandPrimitive.Item>) {
return (
<CommandPrimitive.Item
data-slot="command-item"
className={cn(
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
)
}
function CommandShortcut({
className,
...props
}: ComponentProps<"span">) {
return (
<span
data-slot="command-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className
)}
{...props}
/>
)
}
export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator,
}

View File

@@ -1,130 +0,0 @@
import { ComponentProps } from "react"
import { Drawer as DrawerPrimitive } from "vaul"
import { cn } from "@/lib/utils"
function Drawer({
...props
}: ComponentProps<typeof DrawerPrimitive.Root>) {
return <DrawerPrimitive.Root data-slot="drawer" {...props} />
}
function DrawerTrigger({
...props
}: ComponentProps<typeof DrawerPrimitive.Trigger>) {
return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
}
function DrawerPortal({
...props
}: ComponentProps<typeof DrawerPrimitive.Portal>) {
return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
}
function DrawerClose({
...props
}: ComponentProps<typeof DrawerPrimitive.Close>) {
return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
}
function DrawerOverlay({
className,
...props
}: ComponentProps<typeof DrawerPrimitive.Overlay>) {
return (
<DrawerPrimitive.Overlay
data-slot="drawer-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className
)}
{...props}
/>
)
}
function DrawerContent({
className,
children,
...props
}: ComponentProps<typeof DrawerPrimitive.Content>) {
return (
<DrawerPortal data-slot="drawer-portal">
<DrawerOverlay />
<DrawerPrimitive.Content
data-slot="drawer-content"
className={cn(
"group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
className
)}
{...props}
>
<div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
{children}
</DrawerPrimitive.Content>
</DrawerPortal>
)
}
function DrawerHeader({ className, ...props }: ComponentProps<"div">) {
return (
<div
data-slot="drawer-header"
className={cn("flex flex-col gap-1.5 p-4", className)}
{...props}
/>
)
}
function DrawerFooter({ className, ...props }: ComponentProps<"div">) {
return (
<div
data-slot="drawer-footer"
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props}
/>
)
}
function DrawerTitle({
className,
...props
}: ComponentProps<typeof DrawerPrimitive.Title>) {
return (
<DrawerPrimitive.Title
data-slot="drawer-title"
className={cn("text-foreground font-semibold", className)}
{...props}
/>
)
}
function DrawerDescription({
className,
...props
}: ComponentProps<typeof DrawerPrimitive.Description>) {
return (
<DrawerPrimitive.Description
data-slot="drawer-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
}
export {
Drawer,
DrawerPortal,
DrawerOverlay,
DrawerTrigger,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerFooter,
DrawerTitle,
DrawerDescription,
}

View File

@@ -1,77 +0,0 @@
"use client"
import { ComponentProps, useContext } from "react"
import { OTPInput, OTPInputContext } from "input-otp"
import MinusIcon from "lucide-react/dist/esm/icons/minus"
import { cn } from "@/lib/utils"
function InputOTP({
className,
containerClassName,
...props
}: ComponentProps<typeof OTPInput> & {
containerClassName?: string
}) {
return (
<OTPInput
data-slot="input-otp"
containerClassName={cn(
"flex items-center gap-2 has-disabled:opacity-50",
containerClassName
)}
className={cn("disabled:cursor-not-allowed", className)}
{...props}
/>
)
}
function InputOTPGroup({ className, ...props }: ComponentProps<"div">) {
return (
<div
data-slot="input-otp-group"
className={cn("flex items-center", className)}
{...props}
/>
)
}
function InputOTPSlot({
index,
className,
...props
}: ComponentProps<"div"> & {
index: number
}) {
const inputOTPContext = useContext(OTPInputContext)
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
return (
<div
data-slot="input-otp-slot"
data-active={isActive}
className={cn(
"data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
className
)}
{...props}
>
{char}
{hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
</div>
)}
</div>
)
}
function InputOTPSeparator({ ...props }: ComponentProps<"div">) {
return (
<div data-slot="input-otp-separator" role="separator" {...props}>
<MinusIcon />
</div>
)
}
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }

4
src/scss.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module '*.scss' {
const content: Record<string, string>;
export default content;
}

View File

@@ -1,24 +1,27 @@
@use 'sass:map';
@use './variables' as vars;
// Spacing function
@function spacing($key) {
@return map-get($spacing, $key);
@return map.get(vars.$spacing, $key);
}
// Border radius function
@function radius($key) {
@return map-get($radius, $key);
@return map.get(vars.$radius, $key);
}
// Font size function
@function font-size($key) {
@return map-get($font-sizes, $key);
@return map.get(vars.$font-sizes, $key);
}
// Font weight function
@function font-weight($key) {
@return map-get($font-weights, $key);
@return map.get(vars.$font-weights, $key);
}
// Breakpoint function
@function breakpoint($key) {
@return map-get($breakpoints, $key);
@return map.get(vars.$breakpoints, $key);
}

View File

@@ -1,3 +1,3 @@
@import './variables';
@import './functions';
@import './mixins';
@forward './variables';
@forward './functions';
@forward './mixins';

View File

@@ -1,3 +1,6 @@
@use './functions' as fn;
@use './variables' as vars;
// Flexbox mixins
@mixin flex($direction: row, $justify: flex-start, $align: stretch, $wrap: nowrap) {
display: flex;
@@ -20,7 +23,7 @@
}
// Grid mixin
@mixin grid($columns: 1, $gap: spacing(4)) {
@mixin grid($columns: 1, $gap: fn.spacing(4)) {
display: grid;
grid-template-columns: repeat($columns, 1fr);
gap: $gap;
@@ -28,87 +31,87 @@
// Spacing mixins
@mixin p($value) {
padding: spacing($value);
padding: fn.spacing($value);
}
@mixin px($value) {
padding-left: spacing($value);
padding-right: spacing($value);
padding-left: fn.spacing($value);
padding-right: fn.spacing($value);
}
@mixin py($value) {
padding-top: spacing($value);
padding-bottom: spacing($value);
padding-top: fn.spacing($value);
padding-bottom: fn.spacing($value);
}
@mixin pt($value) {
padding-top: spacing($value);
padding-top: fn.spacing($value);
}
@mixin pr($value) {
padding-right: spacing($value);
padding-right: fn.spacing($value);
}
@mixin pb($value) {
padding-bottom: spacing($value);
padding-bottom: fn.spacing($value);
}
@mixin pl($value) {
padding-left: spacing($value);
padding-left: fn.spacing($value);
}
@mixin m($value) {
margin: spacing($value);
margin: fn.spacing($value);
}
@mixin mx($value) {
margin-left: spacing($value);
margin-right: spacing($value);
margin-left: fn.spacing($value);
margin-right: fn.spacing($value);
}
@mixin my($value) {
margin-top: spacing($value);
margin-bottom: spacing($value);
margin-top: fn.spacing($value);
margin-bottom: fn.spacing($value);
}
@mixin mt($value) {
margin-top: spacing($value);
margin-top: fn.spacing($value);
}
@mixin mr($value) {
margin-right: spacing($value);
margin-right: fn.spacing($value);
}
@mixin mb($value) {
margin-bottom: spacing($value);
margin-bottom: fn.spacing($value);
}
@mixin ml($value) {
margin-left: spacing($value);
margin-left: fn.spacing($value);
}
// Gap mixin
@mixin gap($value) {
gap: spacing($value);
gap: fn.spacing($value);
}
// Border radius mixin
@mixin rounded($value: base) {
border-radius: radius($value);
border-radius: fn.radius($value);
}
// Typography mixins
@mixin text($size) {
font-size: font-size($size);
font-size: fn.font-size($size);
}
@mixin font($weight) {
font-weight: font-weight($weight);
font-weight: fn.font-weight($weight);
}
// Responsive breakpoint mixin
@mixin respond-to($breakpoint) {
$size: breakpoint($breakpoint);
$size: fn.breakpoint($breakpoint);
@media (min-width: $size) {
@content;
}
@@ -124,7 +127,7 @@
display: inline-flex;
align-items: center;
justify-content: center;
gap: spacing(2);
gap: fn.spacing(2);
cursor: pointer;
transition: all 0.2s ease;
border: none;
@@ -138,9 +141,9 @@
// Card mixin
@mixin card {
background: $card;
color: $card-foreground;
border: 1px solid $border;
background: vars.$card;
color: vars.$card-foreground;
border: 1px solid vars.$border;
@include rounded(lg);
@include p(6);
}
@@ -206,7 +209,7 @@
// Focus ring
@mixin focus-ring {
&:focus-visible {
outline: 2px solid $ring;
outline: 2px solid vars.$ring;
outline-offset: 2px;
}
}

View File

@@ -95,7 +95,7 @@ $font-weights: (
semibold: 600,
bold: 700,
extrabold: 800,
black: 900,
"black": 900,
);
// Breakpoints

View File

@@ -1,7 +1,9 @@
@import '../abstracts';
@use '../abstracts' as *;
@use '../abstracts/functions' as fn;
@use '../abstracts/variables' as vars;
// Utility classes generator
@each $name, $value in $spacing {
@each $name, $value in vars.$spacing {
.p-#{$name} { padding: $value !important; }
.px-#{$name} { padding-left: $value !important; padding-right: $value !important; }
.py-#{$name} { padding-top: $value !important; padding-bottom: $value !important; }
@@ -51,17 +53,17 @@
.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)) !important; }
// Border radius
@each $name, $value in $radius {
@each $name, $value in vars.$radius {
.rounded-#{$name} { border-radius: $value !important; }
}
// Text sizes
@each $name, $value in $font-sizes {
@each $name, $value in vars.$font-sizes {
.text-#{$name} { font-size: $value !important; }
}
// Font weights
@each $name, $value in $font-weights {
@each $name, $value in vars.$font-weights {
.font-#{$name} { font-weight: $value !important; }
}
@@ -71,26 +73,26 @@
.text-right { text-align: right !important; }
// Colors
.text-foreground { color: $foreground !important; }
.text-muted-foreground { color: $muted-foreground !important; }
.text-primary { color: $primary !important; }
.text-destructive { color: $destructive !important; }
.text-accent { color: $accent !important; }
.text-foreground { color: vars.$foreground !important; }
.text-muted-foreground { color: vars.$muted-foreground !important; }
.text-primary { color: vars.$primary !important; }
.text-destructive { color: vars.$destructive !important; }
.text-accent { color: vars.$accent !important; }
.bg-background { background-color: $background !important; }
.bg-card { background-color: $card !important; }
.bg-primary { background-color: $primary !important; }
.bg-secondary { background-color: $secondary !important; }
.bg-muted { background-color: $muted !important; }
.bg-accent { background-color: $accent !important; }
.bg-destructive { background-color: $destructive !important; }
.bg-background { background-color: vars.$background !important; }
.bg-card { background-color: vars.$card !important; }
.bg-primary { background-color: vars.$primary !important; }
.bg-secondary { background-color: vars.$secondary !important; }
.bg-muted { background-color: vars.$muted !important; }
.bg-accent { background-color: vars.$accent !important; }
.bg-destructive { background-color: vars.$destructive !important; }
// Border
.border { border: 1px solid $border !important; }
.border-b { border-bottom: 1px solid $border !important; }
.border-t { border-top: 1px solid $border !important; }
.border-l { border-left: 1px solid $border !important; }
.border-r { border-right: 1px solid $border !important; }
.border { border: 1px solid vars.$border !important; }
.border-b { border-bottom: 1px solid vars.$border !important; }
.border-t { border-top: 1px solid vars.$border !important; }
.border-l { border-left: 1px solid vars.$border !important; }
.border-r { border-right: 1px solid vars.$border !important; }
// Width & Height
.w-full { width: 100% !important; }
@@ -137,6 +139,6 @@
// Space between children - DRY loop
$space-values: (2, 3, 4, 6, 8);
@each $value in $space-values {
.space-y-#{$value} > * + * { margin-top: spacing($value) !important; }
.space-x-#{$value} > * + * { margin-left: spacing($value) !important; }
.space-y-#{$value} > * + * { margin-top: fn.spacing($value) !important; }
.space-x-#{$value} > * + * { margin-left: fn.spacing($value) !important; }
}

1
src/vite-end.d.ts vendored
View File

@@ -4,6 +4,7 @@ declare const BASE_KV_SERVICE_URL: string
interface ImportMetaEnv {
readonly VITE_FLASK_BACKEND_URL?: string
readonly DEV?: boolean
}
interface ImportMeta {