Add backup images for login and background removal
- Added loginerps.png.bak (1134 KB) as a backup for the login image. - Added whithoutbackground.png.bak (3455 KB) as a backup for the image without background.
This commit is contained in:
@@ -8,9 +8,9 @@ const buttonVariants = cva(
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-black text-white hover:bg-black/90 active:bg-black",
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary",
|
||||
outline:
|
||||
"border-black bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/20 dark:hover:bg-input/40",
|
||||
"border-primary bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/20 dark:hover:bg-input/40",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_8%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
||||
ghost:
|
||||
|
||||
@@ -78,11 +78,11 @@ export function Combobox<Value>({
|
||||
<ComboboxPrimitive.Item
|
||||
key={String(item.value)}
|
||||
value={item.value}
|
||||
className="relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1.5 pr-8 pl-2.5 text-sm outline-hidden select-none data-highlighted:bg-violet-100 data-highlighted:text-violet-900 dark:data-highlighted:bg-violet-500/25 dark:data-highlighted:text-violet-200"
|
||||
className="relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1.5 pr-8 pl-2.5 text-sm outline-hidden select-none data-highlighted:bg-secondary data-highlighted:text-secondary-foreground"
|
||||
>
|
||||
{item.label}
|
||||
<ComboboxPrimitive.ItemIndicator className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
|
||||
<CheckIcon className="size-4 text-violet-600 dark:text-violet-300" />
|
||||
<CheckIcon className="size-4 text-primary" />
|
||||
</ComboboxPrimitive.ItemIndicator>
|
||||
</ComboboxPrimitive.Item>
|
||||
)}
|
||||
|
||||
@@ -88,7 +88,7 @@ function DropdownMenuItem({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
|
||||
"group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-secondary focus:text-secondary-foreground not-data-[variant=destructive]:focus:**:text-secondary-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -113,7 +113,7 @@ function DropdownMenuSubTrigger({
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-secondary focus:text-secondary-foreground not-data-[variant=destructive]:focus:**:text-secondary-foreground data-inset:pl-7 data-popup-open:bg-secondary data-popup-open:text-secondary-foreground data-open:bg-secondary data-open:text-secondary-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -159,7 +159,7 @@ function DropdownMenuCheckboxItem({
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-secondary focus:text-secondary-foreground focus:**:text-secondary-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
@@ -201,7 +201,7 @@ function DropdownMenuRadioItem({
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-secondary focus:text-secondary-foreground focus:**:text-secondary-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious,
|
||||
} from "@/components/ui/pagination"
|
||||
import { PaginationMeta } from "@/types/common"
|
||||
|
||||
interface PaginationFooterProps {
|
||||
pagination: PaginationMeta
|
||||
onPageChange: (page: number) => void
|
||||
}
|
||||
|
||||
/** First, last, current ±1, and ellipses for the gaps — keeps the row from overflowing
|
||||
* on tables with many pages while still surfacing the ends. */
|
||||
function pageItems(current: number, total: number): (number | "ellipsis")[] {
|
||||
const items: (number | "ellipsis")[] = [1]
|
||||
if (current - 1 > 2) items.push("ellipsis")
|
||||
for (let p = Math.max(2, current - 1); p <= Math.min(total - 1, current + 1); p++) items.push(p)
|
||||
if (current + 1 < total - 1) items.push("ellipsis")
|
||||
if (total > 1) items.push(total)
|
||||
return items
|
||||
}
|
||||
|
||||
/** Server-paginated table footer: "X – Y of Z" plus numbered page links, built on the
|
||||
* shadcn-style pagination primitives. Use for any list page driven by a `PaginationMeta`
|
||||
* from the API (as opposed to `DataTable`'s own client-side pagination). */
|
||||
export function PaginationFooter({ pagination, onPageChange }: PaginationFooterProps) {
|
||||
const { page, pageSize, totalItems, totalPages } = pagination
|
||||
if (totalPages <= 1) return null
|
||||
|
||||
const from = (page - 1) * pageSize + 1
|
||||
const to = Math.min(page * pageSize, totalItems)
|
||||
|
||||
function go(e: React.MouseEvent, target: number) {
|
||||
e.preventDefault()
|
||||
if (target >= 1 && target <= totalPages) onPageChange(target)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{from} – {to} of {totalItems}
|
||||
</p>
|
||||
<Pagination className="mx-0 w-auto justify-end">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
href="#"
|
||||
text=""
|
||||
onClick={(e) => go(e, page - 1)}
|
||||
aria-disabled={page === 1}
|
||||
className={page === 1 ? "pointer-events-none opacity-50" : undefined}
|
||||
/>
|
||||
</PaginationItem>
|
||||
|
||||
{pageItems(page, totalPages).map((item, i) =>
|
||||
item === "ellipsis" ? (
|
||||
<PaginationItem key={`ellipsis-${i}`}>
|
||||
<PaginationEllipsis />
|
||||
</PaginationItem>
|
||||
) : (
|
||||
<PaginationItem key={item}>
|
||||
<PaginationLink href="#" isActive={item === page} onClick={(e) => go(e, item)}>
|
||||
{item}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
)}
|
||||
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
href="#"
|
||||
text=""
|
||||
onClick={(e) => go(e, page + 1)}
|
||||
aria-disabled={page === totalPages}
|
||||
className={page === totalPages ? "pointer-events-none opacity-50" : undefined}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
"use client"
|
||||
|
||||
import { Radio as RadioPrimitive } from "@base-ui/react/radio"
|
||||
import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props<unknown>) {
|
||||
return (
|
||||
<RadioGroupPrimitive
|
||||
data-slot="radio-group"
|
||||
className={cn("flex flex-col gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props<unknown>) {
|
||||
return (
|
||||
<RadioPrimitive.Root
|
||||
data-slot="radio-group-item"
|
||||
className={cn(
|
||||
"relative flex size-4 shrink-0 items-center justify-center rounded-full border border-input transition-colors outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:bg-input/30 data-checked:border-primary",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioPrimitive.Indicator
|
||||
data-slot="radio-group-item-indicator"
|
||||
className="flex items-center justify-center after:size-2 after:rounded-full after:bg-primary"
|
||||
/>
|
||||
</RadioPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { RadioGroup, RadioGroupItem }
|
||||
@@ -150,7 +150,7 @@ function SelectItem({
|
||||
<SelectPrimitive.Item
|
||||
data-slot="select-item"
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-violet-100 focus:text-violet-900 dark:focus:bg-violet-500/25 dark:focus:text-violet-200 not-data-[variant=destructive]:focus:**:text-violet-900 dark:not-data-[variant=destructive]:focus:**:text-violet-200 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-secondary focus:text-secondary-foreground not-data-[variant=destructive]:focus:**:text-secondary-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -163,7 +163,7 @@ function SelectItem({
|
||||
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
|
||||
}
|
||||
>
|
||||
<CheckIcon className="pointer-events-none text-violet-600 dark:text-violet-300" />
|
||||
<CheckIcon className="pointer-events-none text-primary" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</SelectPrimitive.Item>
|
||||
)
|
||||
|
||||
@@ -76,10 +76,11 @@ export function StatCard({
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<p className="text-sm font-medium text-muted-foreground">{label}</p>
|
||||
<p className="text-sm font-medium text-muted-foreground">{label}</p>
|
||||
{Icon && (
|
||||
<div className="flex size-7 shrink-0 items-center justify-center rounded-lg bg-primary/10">
|
||||
<Icon className="size-4 text-primary" />
|
||||
<Icon className="size-3.5 text-primary" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -90,7 +91,7 @@ export function StatCard({
|
||||
</div>
|
||||
|
||||
{hasDelta && (
|
||||
<div className="flex items-center gap-1 text-sm">
|
||||
<div className="flex items-center gap-1 text-xs">
|
||||
<span
|
||||
className={cn(
|
||||
"font-semibold",
|
||||
|
||||
Reference in New Issue
Block a user