diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/susconecta/.gitignore b/susconecta/.gitignore new file mode 100644 index 0000000..f650315 --- /dev/null +++ b/susconecta/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/susconecta/app/dashboard/layout.tsx b/susconecta/app/dashboard/layout.tsx new file mode 100644 index 0000000..51f9872 --- /dev/null +++ b/susconecta/app/dashboard/layout.tsx @@ -0,0 +1,19 @@ +import type React from "react" +import { Sidebar } from "@/components/dashboard/sidebar" +import { DashboardHeader } from "@/components/dashboard/header" + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( +
+ +
+ +
{children}
+
+
+ ) +} diff --git a/susconecta/app/dashboard/pacientes/loading.tsx b/susconecta/app/dashboard/pacientes/loading.tsx new file mode 100644 index 0000000..f15322a --- /dev/null +++ b/susconecta/app/dashboard/pacientes/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return null +} diff --git a/susconecta/app/dashboard/pacientes/page.tsx b/susconecta/app/dashboard/pacientes/page.tsx new file mode 100644 index 0000000..d35bbd1 --- /dev/null +++ b/susconecta/app/dashboard/pacientes/page.tsx @@ -0,0 +1,381 @@ +"use client" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Badge } from "@/components/ui/badge" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { Label } from "@/components/ui/label" +import { Search, Filter, Plus, MoreHorizontal, Calendar, Gift, Eye, Edit, Trash2, CalendarPlus } from "lucide-react" + +const patients = [ + { + id: 1, + name: "Aaron Avalos Perez", + phone: "(75) 99982-6363", + city: "Aracaju", + state: "Sergipe", + lastAppointment: "26/09/2025 14:30", + nextAppointment: "19/08/2025 15:00", + isVip: false, + convenio: "unimed", + birthday: "1985-03-15", + age: 40, + }, + { + id: 2, + name: "ABENANDO OLIVEIRA DE JESUS", + phone: "(75) 99986-0093", + city: "-", + state: "-", + lastAppointment: "Ainda não houve atendimento", + nextAppointment: "Nenhum atendimento agendado", + isVip: false, + convenio: "particular", + birthday: "1978-12-03", + age: 46, + }, + { + id: 3, + name: "ABDIAS DANTAS DOS SANTOS", + phone: "(75) 99125-7267", + city: "São Cristóvão", + state: "Sergipe", + lastAppointment: "30/12/2024 08:40", + nextAppointment: "Nenhum atendimento agendado", + isVip: true, + convenio: "bradesco", + birthday: "1990-12-03", + age: 34, + }, + { + id: 4, + name: "Abdias Matheus Rodrigues Ferreira", + phone: "(75) 99983-7711", + city: "Pirambu", + state: "Sergipe", + lastAppointment: "04/09/2024 16:20", + nextAppointment: "Nenhum atendimento agendado", + isVip: false, + convenio: "amil", + birthday: "1982-12-03", + age: 42, + }, + { + id: 5, + name: "Abdon Ferreira Guerra", + phone: "(75) 99971-0228", + city: "-", + state: "-", + lastAppointment: "08/05/2025 08:00", + nextAppointment: "Nenhum atendimento agendado", + isVip: false, + convenio: "unimed", + birthday: "1975-12-03", + age: 49, + }, +] + +export default function PacientesPage() { + const [searchTerm, setSearchTerm] = useState("") + const [selectedConvenio, setSelectedConvenio] = useState("all") // Updated default value to "all" + const [showVipOnly, setShowVipOnly] = useState(false) + const [showBirthdays, setShowBirthdays] = useState(false) + const [advancedFilters, setAdvancedFilters] = useState({ + city: "", + state: "", + minAge: "", + maxAge: "", + lastAppointmentFrom: "", + lastAppointmentTo: "", + }) + const [isAdvancedFilterOpen, setIsAdvancedFilterOpen] = useState(false) + + const filteredPatients = patients.filter((patient) => { + const matchesSearch = + patient.name.toLowerCase().includes(searchTerm.toLowerCase()) || patient.phone.includes(searchTerm) + + const matchesConvenio = selectedConvenio === "all" || patient.convenio === selectedConvenio + const matchesVip = !showVipOnly || patient.isVip + + // Check if patient has birthday this month + const currentMonth = new Date().getMonth() + 1 + const patientBirthMonth = new Date(patient.birthday).getMonth() + 1 + const matchesBirthday = !showBirthdays || patientBirthMonth === currentMonth + + // Advanced filters + const matchesCity = !advancedFilters.city || patient.city.toLowerCase().includes(advancedFilters.city.toLowerCase()) + const matchesState = + !advancedFilters.state || patient.state.toLowerCase().includes(advancedFilters.state.toLowerCase()) + const matchesMinAge = !advancedFilters.minAge || patient.age >= Number.parseInt(advancedFilters.minAge) + const matchesMaxAge = !advancedFilters.maxAge || patient.age <= Number.parseInt(advancedFilters.maxAge) + + return ( + matchesSearch && + matchesConvenio && + matchesVip && + matchesBirthday && + matchesCity && + matchesState && + matchesMinAge && + matchesMaxAge + ) + }) + + const clearAdvancedFilters = () => { + setAdvancedFilters({ + city: "", + state: "", + minAge: "", + maxAge: "", + lastAppointmentFrom: "", + lastAppointmentTo: "", + }) + } + + const handleViewDetails = (patientId: number) => { + console.log("[v0] Ver detalhes do paciente:", patientId) + // TODO: Navigate to patient details page + } + + const handleEditPatient = (patientId: number) => { + console.log("[v0] Editar paciente:", patientId) + // TODO: Navigate to edit patient form + } + + const handleScheduleAppointment = (patientId: number) => { + console.log("[v0] Marcar consulta para paciente:", patientId) + // TODO: Open appointment scheduling modal + } + + const handleDeletePatient = (patientId: number) => { + console.log("[v0] Excluir paciente:", patientId) + // TODO: Show confirmation dialog and delete patient + } + + return ( +
+ {/* Header */} +
+
+

Pacientes

+

Gerencie as informações de seus pacientes

+
+ +
+ + {/* Filters */} +
+
+ + setSearchTerm(e.target.value)} + className="pl-10" + /> +
+ + + + + + + + + + + + + + Filtros Avançados + + Use os filtros abaixo para refinar sua busca por pacientes específicos. + + +
+
+
+ + setAdvancedFilters((prev) => ({ ...prev, city: e.target.value }))} + placeholder="Digite a cidade" + /> +
+
+ + setAdvancedFilters((prev) => ({ ...prev, state: e.target.value }))} + placeholder="Digite o estado" + /> +
+
+
+
+ + setAdvancedFilters((prev) => ({ ...prev, minAge: e.target.value }))} + placeholder="Ex: 18" + /> +
+
+ + setAdvancedFilters((prev) => ({ ...prev, maxAge: e.target.value }))} + placeholder="Ex: 65" + /> +
+
+
+ + +
+
+
+
+
+ + {/* Table */} +
+ + + + Nome + Telefone + Cidade + Estado + Último atendimento + Próximo atendimento + Ações + + + + {filteredPatients.map((patient) => ( + + +
+
+ {patient.name.charAt(0).toUpperCase()} +
+ + {patient.isVip && ( + + VIP + + )} +
+
+ {patient.phone} + {patient.city} + {patient.state} + + + {patient.lastAppointment} + + + + + {patient.nextAppointment} + + + + + + + + + handleViewDetails(patient.id)}> + + Ver detalhes + + handleEditPatient(patient.id)}> + + Editar + + handleScheduleAppointment(patient.id)}> + + Marcar consulta + + handleDeletePatient(patient.id)} className="text-destructive"> + + Excluir + + + + +
+ ))} +
+
+
+ +
+ Mostrando {filteredPatients.length} de {patients.length} pacientes +
+
+ ) +} diff --git a/susconecta/app/dashboard/page.tsx b/susconecta/app/dashboard/page.tsx new file mode 100644 index 0000000..bf664b9 --- /dev/null +++ b/susconecta/app/dashboard/page.tsx @@ -0,0 +1,29 @@ +export default function DashboardPage() { + return ( +
+
+

Dashboard

+

Bem-vindo ao painel de controle

+
+ +
+
+

Total de Pacientes

+

1,234

+
+
+

Consultas Hoje

+

28

+
+
+

Próximas Consultas

+

45

+
+
+

Receita Mensal

+

R$ 45.230

+
+
+
+ ) +} diff --git a/susconecta/app/globals.css b/susconecta/app/globals.css new file mode 100644 index 0000000..7c20887 --- /dev/null +++ b/susconecta/app/globals.css @@ -0,0 +1,125 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: #ffffff; + --foreground: #475569; + --card: #f8fafc; + --card-foreground: #334155; + --popover: #ffffff; + --popover-foreground: #475569; + --primary: #0f766e; + --primary-foreground: #ffffff; + --secondary: #e2e8f0; + --secondary-foreground: #475569; + --muted: #f1f5f9; + --muted-foreground: #64748b; + --accent: #0891b2; + --accent-foreground: #ffffff; + --destructive: #dc2626; + --destructive-foreground: #ffffff; + --border: #e2e8f0; + --input: #f1f5f9; + --ring: #0f766e; + --chart-1: #0891b2; + --chart-2: #0f766e; + --chart-3: #f59e0b; + --chart-4: #dc2626; + --chart-5: #475569; + --radius: 0.5rem; + --sidebar: #ffffff; + --sidebar-foreground: #475569; + --sidebar-primary: #0f766e; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #0891b2; + --sidebar-accent-foreground: #ffffff; + --sidebar-border: #e2e8f0; + --sidebar-ring: #0f766e; +} + +.dark { + --background: #0f172a; + --foreground: #f1f5f9; + --card: #1e293b; + --card-foreground: #f1f5f9; + --popover: #1e293b; + --popover-foreground: #f1f5f9; + --primary: #14b8a6; + --primary-foreground: #0f172a; + --secondary: #334155; + --secondary-foreground: #f1f5f9; + --muted: #334155; + --muted-foreground: #94a3b8; + --accent: #0891b2; + --accent-foreground: #f1f5f9; + --destructive: #ef4444; + --destructive-foreground: #f1f5f9; + --border: #334155; + --input: #334155; + --ring: #14b8a6; + --chart-1: #0891b2; + --chart-2: #14b8a6; + --chart-3: #f59e0b; + --chart-4: #ef4444; + --chart-5: #94a3b8; + --sidebar: #1e293b; + --sidebar-foreground: #f1f5f9; + --sidebar-primary: #14b8a6; + --sidebar-primary-foreground: #0f172a; + --sidebar-accent: #0891b2; + --sidebar-accent-foreground: #f1f5f9; + --sidebar-border: #334155; + --sidebar-ring: #14b8a6; +} + +@theme inline { + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground font-sans; + } +} diff --git a/susconecta/app/layout.tsx b/susconecta/app/layout.tsx new file mode 100644 index 0000000..853cc31 --- /dev/null +++ b/susconecta/app/layout.tsx @@ -0,0 +1,36 @@ +import type React from "react" +import type { Metadata } from "next" +import { Geist, Geist_Mono } from "next/font/google" +import "./globals.css" + +const geistSans = Geist({ + subsets: ["latin"], + display: "swap", + variable: "--font-geist-sans", +}) + +const geistMono = Geist_Mono({ + subsets: ["latin"], + display: "swap", + variable: "--font-geist-mono", +}) + +export const metadata: Metadata = { + title: "SUSConecta - Conectando Pacientes e Profissionais de Saúde", + description: + "Plataforma inovadora que conecta pacientes e médicos de forma prática, segura e humanizada. Experimente o futuro dos agendamentos médicos.", + keywords: "saúde, médicos, pacientes, agendamento, telemedicina, SUS", + generator: 'v0.app' +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/susconecta/app/page.tsx b/susconecta/app/page.tsx new file mode 100644 index 0000000..3ee76cc --- /dev/null +++ b/susconecta/app/page.tsx @@ -0,0 +1,15 @@ +import { Header } from "@/components/header" +import { HeroSection } from "@/components/hero-section" +import { Footer } from "@/components/footer" + +export default function HomePage() { + return ( +
+
+
+ +
+
+ ) +} diff --git a/susconecta/app/sobre/page.tsx b/susconecta/app/sobre/page.tsx new file mode 100644 index 0000000..4ce80eb --- /dev/null +++ b/susconecta/app/sobre/page.tsx @@ -0,0 +1,15 @@ +import { Header } from "@/components/header" +import { AboutSection } from "@/components/about-section" +import { Footer } from "@/components/footer" + +export default function AboutPage() { + return ( +
+
+
+ +
+
+ ) +} diff --git a/susconecta/components.json b/susconecta/components.json new file mode 100644 index 0000000..4ee62ee --- /dev/null +++ b/susconecta/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/susconecta/components/about-section.tsx b/susconecta/components/about-section.tsx new file mode 100644 index 0000000..1ec12d9 --- /dev/null +++ b/susconecta/components/about-section.tsx @@ -0,0 +1,79 @@ +import { Card } from "@/components/ui/card" +import { Lightbulb, CheckCircle } from "lucide-react" + +export function AboutSection() { + const values = ["Inovação", "Segurança", "Discrição", "Transparência", "Agilidade"] + + return ( +
+
+
+ {/* Left Content */} +
+ {/* Professional Image */} +
+ Profissional trabalhando em laptop em ambiente moderno +
+ + {/* Objective Card */} + +
+
+ +
+
+

NOSSO OBJETIVO

+

+ Nosso compromisso é garantir qualidade, segurança e sigilo em cada atendimento, unindo tecnologia à + responsabilidade médica. +

+
+
+
+
+ + {/* Right Content */} +
+
+
+ SOBRE NÓS +
+

+ Experimente o futuro do gerenciamento dos seus atendimentos médicos +

+
+ +
+

+ Somos uma plataforma inovadora que conecta pacientes e médicos de forma prática, segura e humanizada. + Nosso objetivo é simplificar o processo de emissão e acompanhamento de laudos médicos, oferecendo um + ambiente online confiável e acessível. +

+

+ Aqui, os pacientes podem registrar suas informações de saúde e solicitar laudos de forma rápida, + enquanto os médicos têm acesso a ferramentas que facilitam a análise, validação e emissão dos + documentos. +

+
+ +
+

Nossos valores

+
+ {values.map((value, index) => ( +
+ + {value} +
+ ))} +
+
+
+
+
+
+ ) +} diff --git a/susconecta/components/dashboard/header.tsx b/susconecta/components/dashboard/header.tsx new file mode 100644 index 0000000..30e14a3 --- /dev/null +++ b/susconecta/components/dashboard/header.tsx @@ -0,0 +1,59 @@ +"use client" + +import { Bell, Search } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +export function DashboardHeader() { + return ( +
+
+

NÚCLEO DE ESPECIALIDADES

+
+ +
+
+ + +
+ + + + + + + + + +
+

Dr. Roberto Alves

+

roberto@clinica.com

+
+
+ + Perfil + Configurações + + Sair +
+
+
+
+ ) +} diff --git a/susconecta/components/dashboard/sidebar.tsx b/susconecta/components/dashboard/sidebar.tsx new file mode 100644 index 0000000..f277ea9 --- /dev/null +++ b/susconecta/components/dashboard/sidebar.tsx @@ -0,0 +1,54 @@ +"use client" + +import Link from "next/link" +import { usePathname } from "next/navigation" +import { cn } from "@/lib/utils" +import { Home, Calendar, Users, UserCheck, FileText, BarChart3, Settings, Stethoscope } from "lucide-react" + +const navigation = [ + { name: "Dashboard", href: "/dashboard", icon: Home }, + { name: "Agenda", href: "/dashboard/agenda", icon: Calendar }, + { name: "Pacientes", href: "/dashboard/pacientes", icon: Users }, + { name: "Consultas", href: "/dashboard/consultas", icon: UserCheck }, + { name: "Prontuários", href: "/dashboard/prontuarios", icon: FileText }, + { name: "Relatórios", href: "/dashboard/relatorios", icon: BarChart3 }, + { name: "Configurações", href: "/dashboard/configuracoes", icon: Settings }, +] + +export function Sidebar() { + const pathname = usePathname() + + return ( +
+
+ +
+ +
+ SUSConecta + +
+ + +
+ ) +} diff --git a/susconecta/components/footer.tsx b/susconecta/components/footer.tsx new file mode 100644 index 0000000..c7ecb78 --- /dev/null +++ b/susconecta/components/footer.tsx @@ -0,0 +1,45 @@ +"use client" + +import { ChevronUp } from "lucide-react" +import { Button } from "@/components/ui/button" + +export function Footer() { + const scrollToTop = () => { + window.scrollTo({ top: 0, behavior: "smooth" }) + } + + return ( + + ) +} diff --git a/susconecta/components/header.tsx b/susconecta/components/header.tsx new file mode 100644 index 0000000..5505e4c --- /dev/null +++ b/susconecta/components/header.tsx @@ -0,0 +1,109 @@ +"use client" + +import { useState } from "react" +import Link from "next/link" +import { Button } from "@/components/ui/button" +import { Menu, X } from "lucide-react" + +export function Header() { + const [isMenuOpen, setIsMenuOpen] = useState(false) + + return ( +
+
+
+ {/* Logo */} + + + SUSConecta + + + + {/* Desktop Navigation */} + + + {/* Desktop Action Buttons */} +
+ + + + + + + +
+ + {/* Mobile Menu Button */} + +
+ + {/* Mobile Menu */} + {isMenuOpen && ( +
+ +
+ )} +
+
+ ) +} diff --git a/susconecta/components/hero-section.tsx b/susconecta/components/hero-section.tsx new file mode 100644 index 0000000..ea4dd92 --- /dev/null +++ b/susconecta/components/hero-section.tsx @@ -0,0 +1,84 @@ +import { Button } from "@/components/ui/button" +import { Shield, Clock, Users } from "lucide-react" + +export function HeroSection() { + return ( +
+
+
+ {/* Content */} +
+
+
+ APROXIMANDO MÉDICOS E PACIENTES +
+

+ Segurança, Confiabilidade e{" "} + Rapidez +

+
+

Experimente o futuro dos agendamentos.

+

Encontre profissionais capacitados e marque já sua consulta.

+
+
+ + {/* Action Buttons */} +
+ + +
+
+ + {/* Hero Image */} +
+
+ Médico profissional sorrindo em ambiente médico moderno +
+
+
+ + {/* Features */} +
+
+
+ +
+
+

Laudos digitais e padronizados

+
+
+ +
+
+ +
+
+

Notificações automáticas ao paciente

+
+
+ +
+
+ +
+
+

LGPD: controle de acesso e consentimento

+
+
+
+
+
+ ) +} diff --git a/susconecta/components/theme-provider.tsx b/susconecta/components/theme-provider.tsx new file mode 100644 index 0000000..55c2f6e --- /dev/null +++ b/susconecta/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from 'next-themes' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/susconecta/components/ui/accordion.tsx b/susconecta/components/ui/accordion.tsx new file mode 100644 index 0000000..4a8cca4 --- /dev/null +++ b/susconecta/components/ui/accordion.tsx @@ -0,0 +1,66 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDownIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Accordion({ + ...props +}: React.ComponentProps) { + return +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/susconecta/components/ui/alert-dialog.tsx b/susconecta/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..0863e40 --- /dev/null +++ b/susconecta/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/susconecta/components/ui/alert.tsx b/susconecta/components/ui/alert.tsx new file mode 100644 index 0000000..1421354 --- /dev/null +++ b/susconecta/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/susconecta/components/ui/aspect-ratio.tsx b/susconecta/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..3df3fd0 --- /dev/null +++ b/susconecta/components/ui/aspect-ratio.tsx @@ -0,0 +1,11 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return +} + +export { AspectRatio } diff --git a/susconecta/components/ui/avatar.tsx b/susconecta/components/ui/avatar.tsx new file mode 100644 index 0000000..71e428b --- /dev/null +++ b/susconecta/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/susconecta/components/ui/badge.tsx b/susconecta/components/ui/badge.tsx new file mode 100644 index 0000000..0205413 --- /dev/null +++ b/susconecta/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-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 transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/susconecta/components/ui/breadcrumb.tsx b/susconecta/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..eb88f32 --- /dev/null +++ b/susconecta/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return