biome lint applied
This commit is contained in:
@@ -57,8 +57,8 @@ Draft
|
||||
|
||||
- [ ] Task 5: Configure linting and formatting (AC: 5)
|
||||
- [x] Install Biome (as specified in tech stack, not ESLint+Prettier)
|
||||
- [ ] Create `biome.json` with recommended rules
|
||||
- [ ] Add lint and format scripts to `package.json`
|
||||
- [x] Create `biome.json` with recommended rules
|
||||
- [x] Add lint and format scripts to `package.json`
|
||||
- [ ] Verify linting works on project files
|
||||
|
||||
- [ ] Task 6: Setup Gitea Actions workflow (AC: 6)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { Link } from '@tanstack/react-router';
|
||||
|
||||
import './Header.css'
|
||||
import './Header.css';
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
@@ -23,5 +23,5 @@ export default function Header() {
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,33 @@
|
||||
import * as React from "react";
|
||||
import {
|
||||
Search,
|
||||
Plus,
|
||||
ArrowUpDown,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
ArrowUpDown,
|
||||
} from "lucide-react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
Plus,
|
||||
Search,
|
||||
} from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/collapsible';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -16,106 +35,87 @@ import {
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { cn } from "@/lib/utils";
|
||||
} from '@/components/ui/table';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// Sample data
|
||||
const characters = [
|
||||
{
|
||||
id: 1,
|
||||
nom: "Krosmaster",
|
||||
classe: "Cra",
|
||||
nom: 'Krosmaster',
|
||||
classe: 'Cra',
|
||||
niveau: 200,
|
||||
serveur: "Imagiro",
|
||||
compte: "Compte1",
|
||||
serveur: 'Imagiro',
|
||||
compte: 'Compte1',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
nom: "TankMaster",
|
||||
classe: "Iop",
|
||||
nom: 'TankMaster',
|
||||
classe: 'Iop',
|
||||
niveau: 200,
|
||||
serveur: "Imagiro",
|
||||
compte: "Compte1",
|
||||
serveur: 'Imagiro',
|
||||
compte: 'Compte1',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
nom: "MoneyMaker",
|
||||
classe: "Enu",
|
||||
nom: 'MoneyMaker',
|
||||
classe: 'Enu',
|
||||
niveau: 200,
|
||||
serveur: "Imagiro",
|
||||
compte: "Compte2",
|
||||
serveur: 'Imagiro',
|
||||
compte: 'Compte2',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
nom: "HealBot",
|
||||
classe: "Eni",
|
||||
nom: 'HealBot',
|
||||
classe: 'Eni',
|
||||
niveau: 195,
|
||||
serveur: "Tylezia",
|
||||
compte: "Compte2",
|
||||
serveur: 'Tylezia',
|
||||
compte: 'Compte2',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
nom: "ShadowKill",
|
||||
classe: "Sram",
|
||||
nom: 'ShadowKill',
|
||||
classe: 'Sram',
|
||||
niveau: 200,
|
||||
serveur: "Draconiros",
|
||||
compte: "Compte3",
|
||||
serveur: 'Draconiros',
|
||||
compte: 'Compte3',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
nom: "TimeWarp",
|
||||
classe: "Elio",
|
||||
nom: 'TimeWarp',
|
||||
classe: 'Elio',
|
||||
niveau: 180,
|
||||
serveur: "Imagiro",
|
||||
compte: "Compte1",
|
||||
serveur: 'Imagiro',
|
||||
compte: 'Compte1',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
nom: "ArrowStorm",
|
||||
classe: "Cra",
|
||||
nom: 'ArrowStorm',
|
||||
classe: 'Cra',
|
||||
niveau: 200,
|
||||
serveur: "Tylezia",
|
||||
compte: "Compte4",
|
||||
serveur: 'Tylezia',
|
||||
compte: 'Compte4',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
nom: "BerserkerX",
|
||||
classe: "Iop",
|
||||
nom: 'BerserkerX',
|
||||
classe: 'Iop',
|
||||
niveau: 175,
|
||||
serveur: "Draconiros",
|
||||
compte: "Compte3",
|
||||
serveur: 'Draconiros',
|
||||
compte: 'Compte3',
|
||||
},
|
||||
];
|
||||
|
||||
const classes = [
|
||||
{ name: "Cra", count: 12 },
|
||||
{ name: "Iop", count: 8 },
|
||||
{ name: "Enu", count: 6 },
|
||||
{ name: "Eni", count: 5 },
|
||||
{ name: "Elio", count: 4 },
|
||||
{ name: "Sram", count: 6 },
|
||||
{ name: 'Cra', count: 12 },
|
||||
{ name: 'Iop', count: 8 },
|
||||
{ name: 'Enu', count: 6 },
|
||||
{ name: 'Eni', count: 5 },
|
||||
{ name: 'Elio', count: 4 },
|
||||
{ name: 'Sram', count: 6 },
|
||||
];
|
||||
|
||||
const serveurs = ["Imagiro", "Tylezia", "Draconiros"];
|
||||
const serveurs = ['Imagiro', 'Tylezia', 'Draconiros'];
|
||||
|
||||
export function CharacterList() {
|
||||
const [selectedIds, setSelectedIds] = React.useState<number[]>([]);
|
||||
@@ -286,7 +286,7 @@ export function CharacterList() {
|
||||
<>
|
||||
<span className="text-sm text-[#94A3B8]">
|
||||
{selectedIds.length} sélectionné
|
||||
{selectedIds.length > 1 ? "s" : ""}
|
||||
{selectedIds.length > 1 ? 's' : ''}
|
||||
</span>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
@@ -355,10 +355,10 @@ export function CharacterList() {
|
||||
<TableRow
|
||||
key={character.id}
|
||||
className={cn(
|
||||
"border-[#334155] h-12 transition-colors",
|
||||
'border-[#334155] h-12 transition-colors',
|
||||
isSelected
|
||||
? "bg-[#1E293B] border-l-2 border-l-[#60A5FA]"
|
||||
: "hover:bg-[#1E293B]/50",
|
||||
? 'bg-[#1E293B] border-l-2 border-l-[#60A5FA]'
|
||||
: 'hover:bg-[#1E293B]/50',
|
||||
)}
|
||||
>
|
||||
<TableCell className="w-[40px]">
|
||||
@@ -399,7 +399,7 @@ export function CharacterList() {
|
||||
size="sm"
|
||||
className="border-[#475569] text-[#94A3B8] hover:text-[#F8FAFC] hover:bg-[#334155] rounded-[6px] bg-transparent"
|
||||
>
|
||||
{"<"}
|
||||
{'<'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -427,7 +427,7 @@ export function CharacterList() {
|
||||
size="sm"
|
||||
className="border-[#475569] text-[#94A3B8] hover:text-[#F8FAFC] hover:bg-[#334155] rounded-[6px] bg-transparent"
|
||||
>
|
||||
{">"}
|
||||
{'>'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -5,8 +6,7 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
} from '@/components/ui/dialog';
|
||||
|
||||
interface ConfirmationModalProps {
|
||||
open: boolean;
|
||||
@@ -32,7 +32,7 @@ export function ConfirmationModal({
|
||||
<DialogHeader>
|
||||
<DialogTitle>Confirmer la mise à jour</DialogTitle>
|
||||
<DialogDescription>
|
||||
Marquer {progressionName} comme fait pour {incompleteCount}{" "}
|
||||
Marquer {progressionName} comme fait pour {incompleteCount}{' '}
|
||||
personnages ?
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { useLocation } from "@tanstack/react-router";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Moon, Sun, ChevronRight } from "lucide-react";
|
||||
import { useLocation } from '@tanstack/react-router';
|
||||
import { ChevronRight, Moon, Sun } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
const routeLabels: Record<string, string> = {
|
||||
"/": "Dashboard",
|
||||
"/characters": "Personnages",
|
||||
"/accounts": "Comptes",
|
||||
"/teams": "Teams",
|
||||
"/settings": "Paramètres",
|
||||
'/': 'Dashboard',
|
||||
'/characters': 'Personnages',
|
||||
'/accounts': 'Comptes',
|
||||
'/teams': 'Teams',
|
||||
'/settings': 'Paramètres',
|
||||
};
|
||||
|
||||
interface AppHeaderProps {
|
||||
theme: "dark" | "light";
|
||||
theme: 'dark' | 'light';
|
||||
onToggleTheme: () => void;
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ export function AppHeader({ theme, onToggleTheme }: AppHeaderProps) {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
// Generate breadcrumb from pathname
|
||||
const segments = pathname.split("/").filter(Boolean);
|
||||
const segments = pathname.split('/').filter(Boolean);
|
||||
const breadcrumbs =
|
||||
segments.length === 0
|
||||
? [{ label: "Dashboard", href: "/" }]
|
||||
? [{ label: 'Dashboard', href: '/' }]
|
||||
: segments.map((segment, index) => {
|
||||
const href = "/" + segments.slice(0, index + 1).join("/");
|
||||
const href = '/' + segments.slice(0, index + 1).join('/');
|
||||
const label =
|
||||
routeLabels[href] ||
|
||||
segment.charAt(0).toUpperCase() + segment.slice(1);
|
||||
@@ -43,8 +43,8 @@ export function AppHeader({ theme, onToggleTheme }: AppHeaderProps) {
|
||||
<span
|
||||
className={
|
||||
index === breadcrumbs.length - 1
|
||||
? "font-medium text-foreground"
|
||||
: "text-muted-foreground"
|
||||
? 'font-medium text-foreground'
|
||||
: 'text-muted-foreground'
|
||||
}
|
||||
>
|
||||
{crumb.label}
|
||||
@@ -60,7 +60,7 @@ export function AppHeader({ theme, onToggleTheme }: AppHeaderProps) {
|
||||
onClick={onToggleTheme}
|
||||
className="h-9 w-9"
|
||||
>
|
||||
{theme === "dark" ? (
|
||||
{theme === 'dark' ? (
|
||||
<Sun className="h-5 w-5" />
|
||||
) : (
|
||||
<Moon className="h-5 w-5" />
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type React from "react";
|
||||
import type React from 'react';
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { AppSidebar } from "@/components/layout/app-sidebar";
|
||||
import { AppHeader } from "@/components/layout/app-header";
|
||||
import { useEffect, useState } from 'react';
|
||||
import { AppHeader } from '@/components/layout/app-header';
|
||||
import { AppSidebar } from '@/components/layout/app-sidebar';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface AppShellProps {
|
||||
children: React.ReactNode;
|
||||
@@ -11,20 +11,20 @@ interface AppShellProps {
|
||||
|
||||
export function AppShell({ children }: AppShellProps) {
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
const [theme, setTheme] = useState<"dark" | "light">("dark");
|
||||
const [theme, setTheme] = useState<'dark' | 'light'>('dark');
|
||||
|
||||
// Apply theme class to html element
|
||||
useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
if (theme === "light") {
|
||||
root.classList.add("light");
|
||||
if (theme === 'light') {
|
||||
root.classList.add('light');
|
||||
} else {
|
||||
root.classList.remove("light");
|
||||
root.classList.remove('light');
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme((prev) => (prev === "dark" ? "light" : "dark"));
|
||||
setTheme((prev) => (prev === 'dark' ? 'light' : 'dark'));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -37,8 +37,8 @@ export function AppShell({ children }: AppShellProps) {
|
||||
{/* Main content area */}
|
||||
<div
|
||||
className={cn(
|
||||
"flex min-h-screen flex-col transition-all duration-200",
|
||||
sidebarCollapsed ? "pl-16" : "pl-60",
|
||||
'flex min-h-screen flex-col transition-all duration-200',
|
||||
sidebarCollapsed ? 'pl-16' : 'pl-60',
|
||||
)}
|
||||
>
|
||||
<AppHeader theme={theme} onToggleTheme={toggleTheme} />
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import { Link, useLocation } from "@tanstack/react-router";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Link, useLocation } from '@tanstack/react-router';
|
||||
import {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Folder,
|
||||
Home,
|
||||
Settings,
|
||||
Swords,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import {
|
||||
Home,
|
||||
Users,
|
||||
Folder,
|
||||
Swords,
|
||||
Settings,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
} from "lucide-react";
|
||||
} from '@/components/ui/tooltip';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const navItems = [
|
||||
{ icon: Home, label: "Dashboard", href: "/" },
|
||||
{ icon: Users, label: "Personnages", href: "/characters" },
|
||||
{ icon: Folder, label: "Comptes", href: "/accounts" },
|
||||
{ icon: Swords, label: "Teams", href: "/teams" },
|
||||
{ icon: Settings, label: "Paramètres", href: "/settings" },
|
||||
{ icon: Home, label: 'Dashboard', href: '/' },
|
||||
{ icon: Users, label: 'Personnages', href: '/characters' },
|
||||
{ icon: Folder, label: 'Comptes', href: '/accounts' },
|
||||
{ icon: Swords, label: 'Teams', href: '/teams' },
|
||||
{ icon: Settings, label: 'Paramètres', href: '/settings' },
|
||||
];
|
||||
|
||||
interface AppSidebarProps {
|
||||
@@ -37,8 +37,8 @@ export function AppSidebar({ collapsed, onToggle }: AppSidebarProps) {
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<aside
|
||||
className={cn(
|
||||
"fixed left-0 top-0 z-40 flex h-screen flex-col border-r border-sidebar-border bg-sidebar transition-all duration-200",
|
||||
collapsed ? "w-16" : "w-60",
|
||||
'fixed left-0 top-0 z-40 flex h-screen flex-col border-r border-sidebar-border bg-sidebar transition-all duration-200',
|
||||
collapsed ? 'w-16' : 'w-60',
|
||||
)}
|
||||
>
|
||||
{/* Header with logo and toggle */}
|
||||
@@ -53,8 +53,8 @@ export function AppSidebar({ collapsed, onToggle }: AppSidebarProps) {
|
||||
size="icon"
|
||||
onClick={onToggle}
|
||||
className={cn(
|
||||
"h-8 w-8 text-sidebar-foreground hover:bg-sidebar-accent",
|
||||
collapsed && "mx-auto",
|
||||
'h-8 w-8 text-sidebar-foreground hover:bg-sidebar-accent',
|
||||
collapsed && 'mx-auto',
|
||||
)}
|
||||
>
|
||||
{collapsed ? (
|
||||
@@ -76,10 +76,10 @@ export function AppSidebar({ collapsed, onToggle }: AppSidebarProps) {
|
||||
<Link
|
||||
to={item.href}
|
||||
className={cn(
|
||||
"flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
|
||||
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
|
||||
isActive
|
||||
? "bg-sidebar-primary text-sidebar-primary-foreground"
|
||||
: "text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
||||
? 'bg-sidebar-primary text-sidebar-primary-foreground'
|
||||
: 'text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
|
||||
)}
|
||||
>
|
||||
<Icon className="h-5 w-5 shrink-0" />
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import type React from "react";
|
||||
import {
|
||||
FolderOpen,
|
||||
Users,
|
||||
Swords,
|
||||
Coins,
|
||||
BarChart3,
|
||||
Plus,
|
||||
ChevronDown,
|
||||
ArrowRight,
|
||||
BarChart3,
|
||||
ChevronDown,
|
||||
Coins,
|
||||
FolderOpen,
|
||||
Plus,
|
||||
RefreshCw,
|
||||
} from "lucide-react";
|
||||
Swords,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
import type React from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
} from '@/components/ui/card';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
|
||||
// Custom progress bar with color support
|
||||
function ColoredProgress({
|
||||
@@ -31,12 +31,12 @@ function ColoredProgress({
|
||||
color,
|
||||
}: {
|
||||
value: number;
|
||||
color: "success" | "warning" | "info";
|
||||
color: 'success' | 'warning' | 'info';
|
||||
}) {
|
||||
const colorClasses = {
|
||||
success: "bg-[#4ADE80]",
|
||||
warning: "bg-[#FBBF24]",
|
||||
info: "bg-[#60A5FA]",
|
||||
success: 'bg-[#4ADE80]',
|
||||
warning: 'bg-[#FBBF24]',
|
||||
info: 'bg-[#60A5FA]',
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -57,7 +57,7 @@ function StatCard({
|
||||
secondary,
|
||||
linkText,
|
||||
children,
|
||||
className = "",
|
||||
className = '',
|
||||
}: {
|
||||
icon: React.ElementType;
|
||||
title: string;
|
||||
@@ -101,16 +101,16 @@ function StatCard({
|
||||
export function Dashboard() {
|
||||
// Mock data
|
||||
const currencies = [
|
||||
{ name: "Doplons", amount: "12,450" },
|
||||
{ name: "Orichor", amount: "3,200" },
|
||||
{ name: "Kamas glace", amount: "8,100" },
|
||||
{ name: "Nuggets", amount: "2,340" },
|
||||
{ name: 'Doplons', amount: '12,450' },
|
||||
{ name: 'Orichor', amount: '3,200' },
|
||||
{ name: 'Kamas glace', amount: '8,100' },
|
||||
{ name: 'Nuggets', amount: '2,340' },
|
||||
];
|
||||
|
||||
const progressions = [
|
||||
{ label: "Dofus", value: 72, color: "success" as const },
|
||||
{ label: "Donjons", value: 45, color: "warning" as const },
|
||||
{ label: "Recherchés", value: 61, color: "info" as const },
|
||||
{ label: 'Dofus', value: 72, color: 'success' as const },
|
||||
{ label: 'Donjons', value: 45, color: 'warning' as const },
|
||||
{ label: 'Recherchés', value: 61, color: 'info' as const },
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import { ChevronDown, ChevronRight, Check } from "lucide-react";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Check, ChevronDown, ChevronRight } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import { cn } from "@/lib/utils";
|
||||
} from '@/components/ui/collapsible';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface ProgressionItem {
|
||||
id: string;
|
||||
@@ -18,7 +18,7 @@ interface ProgressionItem {
|
||||
interface ProgressionSectionProps {
|
||||
title: string;
|
||||
items: ProgressionItem[];
|
||||
filter: "all" | "done" | "not-done";
|
||||
filter: 'all' | 'done' | 'not-done';
|
||||
}
|
||||
|
||||
export function ProgressionSection({
|
||||
@@ -30,8 +30,8 @@ export function ProgressionSection({
|
||||
const [localItems, setLocalItems] = useState(items);
|
||||
|
||||
const filteredItems = localItems.filter((item) => {
|
||||
if (filter === "done") return item.completed;
|
||||
if (filter === "not-done") return !item.completed;
|
||||
if (filter === 'done') return item.completed;
|
||||
if (filter === 'not-done') return !item.completed;
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ export function ProgressionSection({
|
||||
...item,
|
||||
completed: !item.completed,
|
||||
completedDate: !item.completed
|
||||
? new Date().toISOString().split("T")[0]
|
||||
? new Date().toISOString().split('T')[0]
|
||||
: undefined,
|
||||
}
|
||||
: item,
|
||||
@@ -84,16 +84,16 @@ export function ProgressionSection({
|
||||
<label
|
||||
htmlFor={item.id}
|
||||
className={cn(
|
||||
"flex-1 text-sm cursor-pointer",
|
||||
item.completed ? "text-foreground" : "text-muted-foreground",
|
||||
'flex-1 text-sm cursor-pointer',
|
||||
item.completed ? 'text-foreground' : 'text-muted-foreground',
|
||||
)}
|
||||
>
|
||||
{item.name}
|
||||
</label>
|
||||
{item.completed ? (
|
||||
<div className="flex items-center gap-1.5 text-sm">
|
||||
<Check className="h-3.5 w-3.5" style={{ color: "#4ADE80" }} />
|
||||
<span style={{ color: "#4ADE80" }}>{item.completedDate}</span>
|
||||
<Check className="h-3.5 w-3.5" style={{ color: '#4ADE80' }} />
|
||||
<span style={{ color: '#4ADE80' }}>{item.completedDate}</span>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-muted-foreground text-sm">—</span>
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
CheckCircle,
|
||||
ChevronRight,
|
||||
Pencil,
|
||||
Trash2,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
} from "lucide-react";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
} from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { ConfirmationModal } from '@/components/confirmation-modal';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Button } from "@/components/ui/button";
|
||||
} from '@/components/ui/select';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -24,32 +25,31 @@ import {
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ConfirmationModal } from "@/components/confirmation-modal";
|
||||
} from '@/components/ui/table';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
|
||||
// Mock data
|
||||
const teamMembers = [
|
||||
{ id: 1, name: "Krosmaster", completed: true, date: "2026-01-10" },
|
||||
{ id: 2, name: "TankMaster", completed: true, date: "2026-01-10" },
|
||||
{ id: 3, name: "HealBot", completed: false, date: null },
|
||||
{ id: 4, name: "SramKiller", completed: false, date: null },
|
||||
{ id: 5, name: "Eniripsa", completed: true, date: "2026-01-09" },
|
||||
{ id: 6, name: "Sacrieur", completed: true, date: "2026-01-08" },
|
||||
{ id: 7, name: "Pandawa", completed: true, date: "2026-01-10" },
|
||||
{ id: 8, name: "Eliotrope", completed: true, date: "2026-01-07" },
|
||||
{ id: 1, name: 'Krosmaster', completed: true, date: '2026-01-10' },
|
||||
{ id: 2, name: 'TankMaster', completed: true, date: '2026-01-10' },
|
||||
{ id: 3, name: 'HealBot', completed: false, date: null },
|
||||
{ id: 4, name: 'SramKiller', completed: false, date: null },
|
||||
{ id: 5, name: 'Eniripsa', completed: true, date: '2026-01-09' },
|
||||
{ id: 6, name: 'Sacrieur', completed: true, date: '2026-01-08' },
|
||||
{ id: 7, name: 'Pandawa', completed: true, date: '2026-01-10' },
|
||||
{ id: 8, name: 'Eliotrope', completed: true, date: '2026-01-07' },
|
||||
];
|
||||
|
||||
const progressions = [
|
||||
{ id: "dofus-turquoise", name: "Dofus Turquoise" },
|
||||
{ id: "donjon-bethel", name: "Donjon Bethel" },
|
||||
{ id: "quete-ebene", name: "Quête Ébène" },
|
||||
{ id: "succes-dimension", name: "Succès Dimension" },
|
||||
{ id: 'dofus-turquoise', name: 'Dofus Turquoise' },
|
||||
{ id: 'donjon-bethel', name: 'Donjon Bethel' },
|
||||
{ id: 'quete-ebene', name: 'Quête Ébène' },
|
||||
{ id: 'succes-dimension', name: 'Succès Dimension' },
|
||||
];
|
||||
|
||||
export default function TeamDetailPage() {
|
||||
const [selectedProgression, setSelectedProgression] =
|
||||
useState("dofus-turquoise");
|
||||
useState('dofus-turquoise');
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const completedCount = teamMembers.filter((m) => m.completed).length;
|
||||
@@ -58,7 +58,7 @@ export default function TeamDetailPage() {
|
||||
const progressPercentage = Math.round((completedCount / totalCount) * 100);
|
||||
|
||||
const selectedProgressionName =
|
||||
progressions.find((p) => p.id === selectedProgression)?.name || "";
|
||||
progressions.find((p) => p.id === selectedProgression)?.name || '';
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background p-6">
|
||||
@@ -201,7 +201,7 @@ export default function TeamDetailPage() {
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="py-2 text-muted-foreground">
|
||||
{member.date || "—"}
|
||||
{member.date || '—'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import type * as React from 'react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
destructive:
|
||||
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
"icon-sm": "size-8",
|
||||
"icon-lg": "size-10",
|
||||
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
||||
sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
|
||||
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
||||
icon: 'size-9',
|
||||
'icon-sm': 'size-8',
|
||||
'icon-lg': 'size-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
}: React.ComponentProps<'button'> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean
|
||||
asChild?: boolean;
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
const Comp = asChild ? Slot : 'button';
|
||||
|
||||
return (
|
||||
<Comp
|
||||
@@ -56,7 +56,7 @@ function Button({
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Button, buttonVariants }
|
||||
export { Button, buttonVariants };
|
||||
|
||||
@@ -1,84 +1,84 @@
|
||||
import * as React from "react"
|
||||
import type * as React from 'react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function Card({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||
className
|
||||
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
className={cn('leading-none font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-action"
|
||||
className={cn(
|
||||
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
||||
className
|
||||
'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn("px-6", className)}
|
||||
className={cn('px-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -89,4 +89,4 @@ export {
|
||||
CardAction,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import * as React from "react"
|
||||
import type * as React from 'react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className
|
||||
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Input }
|
||||
export { Input };
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from "react"
|
||||
import type * as React from 'react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||
function Table({ className, ...props }: React.ComponentProps<'table'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="table-container"
|
||||
@@ -10,96 +10,96 @@ function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||
>
|
||||
<table
|
||||
data-slot="table"
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
className={cn('w-full caption-bottom text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
||||
function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
|
||||
return (
|
||||
<thead
|
||||
data-slot="table-header"
|
||||
className={cn("[&_tr]:border-b", className)}
|
||||
className={cn('[&_tr]:border-b', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
||||
function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
|
||||
return (
|
||||
<tbody
|
||||
data-slot="table-body"
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
className={cn('[&_tr:last-child]:border-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
||||
function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
|
||||
return (
|
||||
<tfoot
|
||||
data-slot="table-footer"
|
||||
className={cn(
|
||||
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
||||
className
|
||||
'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
||||
function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
|
||||
return (
|
||||
<tr
|
||||
data-slot="table-row"
|
||||
className={cn(
|
||||
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
||||
className
|
||||
'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
||||
function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
|
||||
return (
|
||||
<th
|
||||
data-slot="table-head"
|
||||
className={cn(
|
||||
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
'text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
||||
function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
|
||||
return (
|
||||
<td
|
||||
data-slot="table-cell"
|
||||
className={cn(
|
||||
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TableCaption({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"caption">) {
|
||||
}: React.ComponentProps<'caption'>) {
|
||||
return (
|
||||
<caption
|
||||
data-slot="table-caption"
|
||||
className={cn("text-muted-foreground mt-4 text-sm", className)}
|
||||
className={cn('text-muted-foreground mt-4 text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -111,4 +111,4 @@ export {
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createServerFn } from '@tanstack/react-start'
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
|
||||
export const getPunkSongs = createServerFn({
|
||||
method: 'GET',
|
||||
@@ -10,4 +10,4 @@ export const getPunkSongs = createServerFn({
|
||||
{ id: 5, name: 'Fat Lip', artist: 'Sum 41' },
|
||||
{ id: 6, name: 'All the Small Things', artist: 'blink-182' },
|
||||
{ id: 7, name: 'Beverly Hills', artist: 'Weezer' },
|
||||
])
|
||||
]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined;
|
||||
@@ -8,11 +8,11 @@ export const prisma =
|
||||
globalForPrisma.prisma ??
|
||||
new PrismaClient({
|
||||
log:
|
||||
process.env.NODE_ENV === "development"
|
||||
? ["query", "error", "warn"]
|
||||
: ["error"],
|
||||
process.env.NODE_ENV === 'development'
|
||||
? ['query', 'error', 'warn']
|
||||
: ['error'],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
globalForPrisma.prisma = prisma;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
@@ -8,90 +8,90 @@
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as DemoStartServerFuncsRouteImport } from './routes/demo/start.server-funcs'
|
||||
import { Route as DemoStartApiRequestRouteImport } from './routes/demo/start.api-request'
|
||||
import { Route as DemoApiNamesRouteImport } from './routes/demo/api.names'
|
||||
import { Route as DemoStartSsrIndexRouteImport } from './routes/demo/start.ssr.index'
|
||||
import { Route as DemoStartSsrSpaModeRouteImport } from './routes/demo/start.ssr.spa-mode'
|
||||
import { Route as DemoStartSsrFullSsrRouteImport } from './routes/demo/start.ssr.full-ssr'
|
||||
import { Route as DemoStartSsrDataOnlyRouteImport } from './routes/demo/start.ssr.data-only'
|
||||
import { Route as rootRouteImport } from './routes/__root';
|
||||
import { Route as DemoApiNamesRouteImport } from './routes/demo/api.names';
|
||||
import { Route as DemoStartApiRequestRouteImport } from './routes/demo/start.api-request';
|
||||
import { Route as DemoStartServerFuncsRouteImport } from './routes/demo/start.server-funcs';
|
||||
import { Route as DemoStartSsrDataOnlyRouteImport } from './routes/demo/start.ssr.data-only';
|
||||
import { Route as DemoStartSsrFullSsrRouteImport } from './routes/demo/start.ssr.full-ssr';
|
||||
import { Route as DemoStartSsrIndexRouteImport } from './routes/demo/start.ssr.index';
|
||||
import { Route as DemoStartSsrSpaModeRouteImport } from './routes/demo/start.ssr.spa-mode';
|
||||
import { Route as IndexRouteImport } from './routes/index';
|
||||
|
||||
const IndexRoute = IndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DemoStartServerFuncsRoute = DemoStartServerFuncsRouteImport.update({
|
||||
id: '/demo/start/server-funcs',
|
||||
path: '/demo/start/server-funcs',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DemoStartApiRequestRoute = DemoStartApiRequestRouteImport.update({
|
||||
id: '/demo/start/api-request',
|
||||
path: '/demo/start/api-request',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DemoApiNamesRoute = DemoApiNamesRouteImport.update({
|
||||
id: '/demo/api/names',
|
||||
path: '/demo/api/names',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DemoStartSsrIndexRoute = DemoStartSsrIndexRouteImport.update({
|
||||
id: '/demo/start/ssr/',
|
||||
path: '/demo/start/ssr/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DemoStartSsrSpaModeRoute = DemoStartSsrSpaModeRouteImport.update({
|
||||
id: '/demo/start/ssr/spa-mode',
|
||||
path: '/demo/start/ssr/spa-mode',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DemoStartSsrFullSsrRoute = DemoStartSsrFullSsrRouteImport.update({
|
||||
id: '/demo/start/ssr/full-ssr',
|
||||
path: '/demo/start/ssr/full-ssr',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
const DemoStartSsrDataOnlyRoute = DemoStartSsrDataOnlyRouteImport.update({
|
||||
id: '/demo/start/ssr/data-only',
|
||||
path: '/demo/start/ssr/data-only',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
} as any);
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/demo/api/names': typeof DemoApiNamesRoute
|
||||
'/demo/start/api-request': typeof DemoStartApiRequestRoute
|
||||
'/demo/start/server-funcs': typeof DemoStartServerFuncsRoute
|
||||
'/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute
|
||||
'/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute
|
||||
'/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute
|
||||
'/demo/start/ssr/': typeof DemoStartSsrIndexRoute
|
||||
'/': typeof IndexRoute;
|
||||
'/demo/api/names': typeof DemoApiNamesRoute;
|
||||
'/demo/start/api-request': typeof DemoStartApiRequestRoute;
|
||||
'/demo/start/server-funcs': typeof DemoStartServerFuncsRoute;
|
||||
'/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute;
|
||||
'/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute;
|
||||
'/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute;
|
||||
'/demo/start/ssr/': typeof DemoStartSsrIndexRoute;
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/demo/api/names': typeof DemoApiNamesRoute
|
||||
'/demo/start/api-request': typeof DemoStartApiRequestRoute
|
||||
'/demo/start/server-funcs': typeof DemoStartServerFuncsRoute
|
||||
'/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute
|
||||
'/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute
|
||||
'/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute
|
||||
'/demo/start/ssr': typeof DemoStartSsrIndexRoute
|
||||
'/': typeof IndexRoute;
|
||||
'/demo/api/names': typeof DemoApiNamesRoute;
|
||||
'/demo/start/api-request': typeof DemoStartApiRequestRoute;
|
||||
'/demo/start/server-funcs': typeof DemoStartServerFuncsRoute;
|
||||
'/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute;
|
||||
'/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute;
|
||||
'/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute;
|
||||
'/demo/start/ssr': typeof DemoStartSsrIndexRoute;
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/demo/api/names': typeof DemoApiNamesRoute
|
||||
'/demo/start/api-request': typeof DemoStartApiRequestRoute
|
||||
'/demo/start/server-funcs': typeof DemoStartServerFuncsRoute
|
||||
'/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute
|
||||
'/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute
|
||||
'/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute
|
||||
'/demo/start/ssr/': typeof DemoStartSsrIndexRoute
|
||||
__root__: typeof rootRouteImport;
|
||||
'/': typeof IndexRoute;
|
||||
'/demo/api/names': typeof DemoApiNamesRoute;
|
||||
'/demo/start/api-request': typeof DemoStartApiRequestRoute;
|
||||
'/demo/start/server-funcs': typeof DemoStartServerFuncsRoute;
|
||||
'/demo/start/ssr/data-only': typeof DemoStartSsrDataOnlyRoute;
|
||||
'/demo/start/ssr/full-ssr': typeof DemoStartSsrFullSsrRoute;
|
||||
'/demo/start/ssr/spa-mode': typeof DemoStartSsrSpaModeRoute;
|
||||
'/demo/start/ssr/': typeof DemoStartSsrIndexRoute;
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fileRoutesByFullPath: FileRoutesByFullPath;
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/demo/api/names'
|
||||
@@ -100,8 +100,8 @@ export interface FileRouteTypes {
|
||||
| '/demo/start/ssr/data-only'
|
||||
| '/demo/start/ssr/full-ssr'
|
||||
| '/demo/start/ssr/spa-mode'
|
||||
| '/demo/start/ssr/'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
| '/demo/start/ssr/';
|
||||
fileRoutesByTo: FileRoutesByTo;
|
||||
to:
|
||||
| '/'
|
||||
| '/demo/api/names'
|
||||
@@ -110,7 +110,7 @@ export interface FileRouteTypes {
|
||||
| '/demo/start/ssr/data-only'
|
||||
| '/demo/start/ssr/full-ssr'
|
||||
| '/demo/start/ssr/spa-mode'
|
||||
| '/demo/start/ssr'
|
||||
| '/demo/start/ssr';
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
@@ -120,78 +120,78 @@ export interface FileRouteTypes {
|
||||
| '/demo/start/ssr/data-only'
|
||||
| '/demo/start/ssr/full-ssr'
|
||||
| '/demo/start/ssr/spa-mode'
|
||||
| '/demo/start/ssr/'
|
||||
fileRoutesById: FileRoutesById
|
||||
| '/demo/start/ssr/';
|
||||
fileRoutesById: FileRoutesById;
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
DemoApiNamesRoute: typeof DemoApiNamesRoute
|
||||
DemoStartApiRequestRoute: typeof DemoStartApiRequestRoute
|
||||
DemoStartServerFuncsRoute: typeof DemoStartServerFuncsRoute
|
||||
DemoStartSsrDataOnlyRoute: typeof DemoStartSsrDataOnlyRoute
|
||||
DemoStartSsrFullSsrRoute: typeof DemoStartSsrFullSsrRoute
|
||||
DemoStartSsrSpaModeRoute: typeof DemoStartSsrSpaModeRoute
|
||||
DemoStartSsrIndexRoute: typeof DemoStartSsrIndexRoute
|
||||
IndexRoute: typeof IndexRoute;
|
||||
DemoApiNamesRoute: typeof DemoApiNamesRoute;
|
||||
DemoStartApiRequestRoute: typeof DemoStartApiRequestRoute;
|
||||
DemoStartServerFuncsRoute: typeof DemoStartServerFuncsRoute;
|
||||
DemoStartSsrDataOnlyRoute: typeof DemoStartSsrDataOnlyRoute;
|
||||
DemoStartSsrFullSsrRoute: typeof DemoStartSsrFullSsrRoute;
|
||||
DemoStartSsrSpaModeRoute: typeof DemoStartSsrSpaModeRoute;
|
||||
DemoStartSsrIndexRoute: typeof DemoStartSsrIndexRoute;
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
id: '/';
|
||||
path: '/';
|
||||
fullPath: '/';
|
||||
preLoaderRoute: typeof IndexRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
'/demo/start/server-funcs': {
|
||||
id: '/demo/start/server-funcs'
|
||||
path: '/demo/start/server-funcs'
|
||||
fullPath: '/demo/start/server-funcs'
|
||||
preLoaderRoute: typeof DemoStartServerFuncsRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
id: '/demo/start/server-funcs';
|
||||
path: '/demo/start/server-funcs';
|
||||
fullPath: '/demo/start/server-funcs';
|
||||
preLoaderRoute: typeof DemoStartServerFuncsRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
'/demo/start/api-request': {
|
||||
id: '/demo/start/api-request'
|
||||
path: '/demo/start/api-request'
|
||||
fullPath: '/demo/start/api-request'
|
||||
preLoaderRoute: typeof DemoStartApiRequestRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
id: '/demo/start/api-request';
|
||||
path: '/demo/start/api-request';
|
||||
fullPath: '/demo/start/api-request';
|
||||
preLoaderRoute: typeof DemoStartApiRequestRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
'/demo/api/names': {
|
||||
id: '/demo/api/names'
|
||||
path: '/demo/api/names'
|
||||
fullPath: '/demo/api/names'
|
||||
preLoaderRoute: typeof DemoApiNamesRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
id: '/demo/api/names';
|
||||
path: '/demo/api/names';
|
||||
fullPath: '/demo/api/names';
|
||||
preLoaderRoute: typeof DemoApiNamesRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
'/demo/start/ssr/': {
|
||||
id: '/demo/start/ssr/'
|
||||
path: '/demo/start/ssr'
|
||||
fullPath: '/demo/start/ssr/'
|
||||
preLoaderRoute: typeof DemoStartSsrIndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
id: '/demo/start/ssr/';
|
||||
path: '/demo/start/ssr';
|
||||
fullPath: '/demo/start/ssr/';
|
||||
preLoaderRoute: typeof DemoStartSsrIndexRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
'/demo/start/ssr/spa-mode': {
|
||||
id: '/demo/start/ssr/spa-mode'
|
||||
path: '/demo/start/ssr/spa-mode'
|
||||
fullPath: '/demo/start/ssr/spa-mode'
|
||||
preLoaderRoute: typeof DemoStartSsrSpaModeRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
id: '/demo/start/ssr/spa-mode';
|
||||
path: '/demo/start/ssr/spa-mode';
|
||||
fullPath: '/demo/start/ssr/spa-mode';
|
||||
preLoaderRoute: typeof DemoStartSsrSpaModeRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
'/demo/start/ssr/full-ssr': {
|
||||
id: '/demo/start/ssr/full-ssr'
|
||||
path: '/demo/start/ssr/full-ssr'
|
||||
fullPath: '/demo/start/ssr/full-ssr'
|
||||
preLoaderRoute: typeof DemoStartSsrFullSsrRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
id: '/demo/start/ssr/full-ssr';
|
||||
path: '/demo/start/ssr/full-ssr';
|
||||
fullPath: '/demo/start/ssr/full-ssr';
|
||||
preLoaderRoute: typeof DemoStartSsrFullSsrRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
'/demo/start/ssr/data-only': {
|
||||
id: '/demo/start/ssr/data-only'
|
||||
path: '/demo/start/ssr/data-only'
|
||||
fullPath: '/demo/start/ssr/data-only'
|
||||
preLoaderRoute: typeof DemoStartSsrDataOnlyRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
id: '/demo/start/ssr/data-only';
|
||||
path: '/demo/start/ssr/data-only';
|
||||
fullPath: '/demo/start/ssr/data-only';
|
||||
preLoaderRoute: typeof DemoStartSsrDataOnlyRouteImport;
|
||||
parentRoute: typeof rootRouteImport;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,16 +204,17 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
DemoStartSsrFullSsrRoute: DemoStartSsrFullSsrRoute,
|
||||
DemoStartSsrSpaModeRoute: DemoStartSsrSpaModeRoute,
|
||||
DemoStartSsrIndexRoute: DemoStartSsrIndexRoute,
|
||||
}
|
||||
};
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
._addFileTypes<FileRouteTypes>();
|
||||
|
||||
import type { createStart } from '@tanstack/react-start';
|
||||
import type { getRouter } from './router.tsx';
|
||||
|
||||
import type { getRouter } from './router.tsx'
|
||||
import type { createStart } from '@tanstack/react-start'
|
||||
declare module '@tanstack/react-start' {
|
||||
interface Register {
|
||||
ssr: true
|
||||
router: Awaited<ReturnType<typeof getRouter>>
|
||||
ssr: true;
|
||||
router: Awaited<ReturnType<typeof getRouter>>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createRouter } from '@tanstack/react-router'
|
||||
import { createRouter } from '@tanstack/react-router';
|
||||
|
||||
// Import the generated route tree
|
||||
import { routeTree } from './routeTree.gen'
|
||||
import { routeTree } from './routeTree.gen';
|
||||
|
||||
// Create a new router instance
|
||||
export const getRouter = () => {
|
||||
@@ -11,7 +11,7 @@ export const getRouter = () => {
|
||||
|
||||
scrollRestoration: true,
|
||||
defaultPreloadStaleTime: 0,
|
||||
})
|
||||
});
|
||||
|
||||
return router
|
||||
}
|
||||
return router;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
|
||||
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
|
||||
import { TanStackDevtools } from '@tanstack/react-devtools'
|
||||
import { TanStackDevtools } from '@tanstack/react-devtools';
|
||||
import { createRootRoute, HeadContent, Scripts } from '@tanstack/react-router';
|
||||
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
|
||||
|
||||
import Header from '../components/Header'
|
||||
import Header from '../components/Header';
|
||||
|
||||
import appCss from '../styles.css?url'
|
||||
import appCss from '../styles.css?url';
|
||||
|
||||
export const Route = createRootRoute({
|
||||
head: () => ({
|
||||
@@ -29,7 +29,7 @@ export const Route = createRootRoute({
|
||||
}),
|
||||
|
||||
shellComponent: RootDocument,
|
||||
})
|
||||
});
|
||||
|
||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
@@ -54,5 +54,5 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { createAPIFileRoute } from "@tanstack/start/api";
|
||||
import { prisma } from "@/lib/server/db";
|
||||
import { createAPIFileRoute } from '@tanstack/start/api';
|
||||
import { prisma } from '@/lib/server/db';
|
||||
|
||||
export const Route = createAPIFileRoute("/api/health")({
|
||||
export const Route = createAPIFileRoute('/api/health')({
|
||||
GET: async () => {
|
||||
let dbStatus = "disconnected";
|
||||
let dbStatus = 'disconnected';
|
||||
try {
|
||||
await prisma.$queryRaw`SELECT 1`;
|
||||
dbStatus = "connected";
|
||||
dbStatus = 'connected';
|
||||
} catch {
|
||||
dbStatus = "error";
|
||||
dbStatus = 'error';
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
status: "ok",
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
database: dbStatus,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { json } from '@tanstack/react-start'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { json } from '@tanstack/react-start';
|
||||
|
||||
export const Route = createFileRoute('/demo/api/names')({
|
||||
server: {
|
||||
@@ -7,4 +7,4 @@ export const Route = createFileRoute('/demo/api/names')({
|
||||
GET: () => json(['Alice', 'Bob', 'Charlie']),
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import './start.css'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import './start.css';
|
||||
|
||||
function getNames() {
|
||||
return fetch('/demo/api/names').then((res) => res.json() as Promise<string[]>)
|
||||
return fetch('/demo/api/names').then(
|
||||
(res) => res.json() as Promise<string[]>,
|
||||
);
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/demo/start/api-request')({
|
||||
component: Home,
|
||||
})
|
||||
});
|
||||
|
||||
function Home() {
|
||||
const [names, setNames] = useState<Array<string>>([])
|
||||
const [names, setNames] = useState<Array<string>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getNames().then(setNames)
|
||||
}, [])
|
||||
getNames().then(setNames);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="api-page">
|
||||
@@ -29,5 +30,5 @@ function Home() {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import fs from 'node:fs'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { createFileRoute, useRouter } from '@tanstack/react-router'
|
||||
import { createServerFn } from '@tanstack/react-start'
|
||||
import './start.css'
|
||||
import fs from 'node:fs';
|
||||
import { createFileRoute, useRouter } from '@tanstack/react-router';
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { useCallback, useState } from 'react';
|
||||
import './start.css';
|
||||
|
||||
/*
|
||||
const loggingMiddleware = createMiddleware().server(
|
||||
@@ -16,7 +16,7 @@ const loggedServerFunction = createServerFn({ method: "GET" }).middleware([
|
||||
]);
|
||||
*/
|
||||
|
||||
const TODOS_FILE = 'todos.json'
|
||||
const TODOS_FILE = 'todos.json';
|
||||
|
||||
async function readTodos() {
|
||||
return JSON.parse(
|
||||
@@ -30,38 +30,38 @@ async function readTodos() {
|
||||
2,
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const getTodos = createServerFn({
|
||||
method: 'GET',
|
||||
}).handler(async () => await readTodos())
|
||||
}).handler(async () => await readTodos());
|
||||
|
||||
const addTodo = createServerFn({ method: 'POST' })
|
||||
.inputValidator((d: string) => d)
|
||||
.handler(async ({ data }) => {
|
||||
const todos = await readTodos()
|
||||
todos.push({ id: todos.length + 1, name: data })
|
||||
await fs.promises.writeFile(TODOS_FILE, JSON.stringify(todos, null, 2))
|
||||
return todos
|
||||
})
|
||||
const todos = await readTodos();
|
||||
todos.push({ id: todos.length + 1, name: data });
|
||||
await fs.promises.writeFile(TODOS_FILE, JSON.stringify(todos, null, 2));
|
||||
return todos;
|
||||
});
|
||||
|
||||
export const Route = createFileRoute('/demo/start/server-funcs')({
|
||||
component: Home,
|
||||
loader: async () => await getTodos(),
|
||||
})
|
||||
});
|
||||
|
||||
function Home() {
|
||||
const router = useRouter()
|
||||
let todos = Route.useLoaderData()
|
||||
const router = useRouter();
|
||||
let todos = Route.useLoaderData();
|
||||
|
||||
const [todo, setTodo] = useState('')
|
||||
const [todo, setTodo] = useState('');
|
||||
|
||||
const submitTodo = useCallback(async () => {
|
||||
todos = await addTodo({ data: todo })
|
||||
setTodo('')
|
||||
router.invalidate()
|
||||
}, [addTodo, todo])
|
||||
todos = await addTodo({ data: todo });
|
||||
setTodo('');
|
||||
router.invalidate();
|
||||
}, [addTodo, todo]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -78,7 +78,7 @@ function Home() {
|
||||
onChange={(e) => setTodo(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
submitTodo()
|
||||
submitTodo();
|
||||
}
|
||||
}}
|
||||
placeholder="Enter a new todo..."
|
||||
@@ -88,5 +88,5 @@ function Home() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { getPunkSongs } from '@/data/demo.punk-songs'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { getPunkSongs } from '@/data/demo.punk-songs';
|
||||
|
||||
export const Route = createFileRoute('/demo/start/ssr/data-only')({
|
||||
ssr: 'data-only',
|
||||
component: RouteComponent,
|
||||
loader: async () => await getPunkSongs(),
|
||||
})
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const punkSongs = Route.useLoaderData()
|
||||
const punkSongs = Route.useLoaderData();
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -21,5 +21,5 @@ function RouteComponent() {
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { getPunkSongs } from '@/data/demo.punk-songs'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { getPunkSongs } from '@/data/demo.punk-songs';
|
||||
|
||||
export const Route = createFileRoute('/demo/start/ssr/full-ssr')({
|
||||
component: RouteComponent,
|
||||
loader: async () => await getPunkSongs(),
|
||||
})
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const punkSongs = Route.useLoaderData()
|
||||
const punkSongs = Route.useLoaderData();
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -20,5 +20,5 @@ function RouteComponent() {
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createFileRoute, Link } from '@tanstack/react-router'
|
||||
import { createFileRoute, Link } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/demo/start/ssr/')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
@@ -20,5 +20,5 @@ function RouteComponent() {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { getPunkSongs } from '@/data/demo.punk-songs'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getPunkSongs } from '@/data/demo.punk-songs';
|
||||
|
||||
export const Route = createFileRoute('/demo/start/ssr/spa-mode')({
|
||||
ssr: false,
|
||||
component: RouteComponent,
|
||||
})
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const [punkSongs, setPunkSongs] = useState<
|
||||
Awaited<ReturnType<typeof getPunkSongs>>
|
||||
>([])
|
||||
>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getPunkSongs().then(setPunkSongs)
|
||||
}, [])
|
||||
getPunkSongs().then(setPunkSongs);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -27,5 +27,5 @@ function RouteComponent() {
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import '../App.css'
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import '../App.css';
|
||||
|
||||
export const Route = createFileRoute('/')({ component: App })
|
||||
export const Route = createFileRoute('/')({ component: App });
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -33,5 +33,5 @@ function App() {
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user