[MVPTN-2] feat(CRUD): CRUD com Mock para gerenciar a tabela t_tb_andamentoservico

This commit is contained in:
Keven Willian Pereira de Souza 2025-09-11 17:13:55 -03:00
parent 02ae936c87
commit 11aff8e15a
16 changed files with 1057 additions and 66 deletions

88
package-lock.json generated
View file

@ -16,6 +16,8 @@
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.8",
@ -732,6 +734,12 @@
"node": ">= 10"
}
},
"node_modules/@radix-ui/number": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
"integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
"license": "MIT"
},
"node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
@ -1160,6 +1168,43 @@
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
"integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-focus-guards": "1.1.3",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
@ -1294,6 +1339,49 @@
}
}
},
"node_modules/@radix-ui/react-select": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
"integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.1",
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-focus-guards": "1.1.3",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-visually-hidden": "1.2.3",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-separator": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",

View file

@ -17,6 +17,8 @@
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.8",

View file

@ -0,0 +1,93 @@
'use client'
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { useEffect, useState } from "react";
import { useTTBAndamentoServicoReadHook } from "../../_hooks/t_tb_andamentoservico/useTTBAndamentoServicoReadHook";
import Loading from "@/app/_components/loading/loading";
import TTBAndamentoServicoInteface, { tipoEnum } from "../../_interfaces/TTBAndamentoServicoInterface";
import { TTBAndamentoServicoSchema } from "../../_schemas/TTBAndamentoServico";
import z from "zod";
import { useTTBAndamentoServicoSaveHook } from "../../_hooks/t_tb_andamentoservico/useTTBAndamentoServicoSaveHook";
import TTBAndamentoServicoTable from "../../_components/t_tb_andamentoservico/TTBAndamentoServicoTable";
import TTBAndamentoServicoForm from "../../_components/t_tb_andamentoservico/TTBAndamentoServicoForm";
import TTBAndamentoServicoAlert from "../../_components/t_tb_andamentoservico/TTBAndamentoServicoAlert";
import { PlusIcon } from "lucide-react";
type FormValues = z.infer<typeof TTBAndamentoServicoSchema>
export default function TTBAndamentoServico() {
const { tTBAndamentosServicos, fetchTTBAndamentoServico, addTTBAndamentoServico } = useTTBAndamentoServicoReadHook();
const { tTBAndamentoServico, saveTTBAndamentoServico } = useTTBAndamentoServicoSaveHook();
const [dialogOpen, setDialogOpen] = useState(false);
const [alertDialogOpen, setAlertDialogOpen] = useState(false);
const [item, setItem] = useState<TTBAndamentoServicoInteface | null>(null);
const [formOpen, setFormOpen] = useState(false);
const [editingItem, setEditingItem] = useState<FormValues | null>(null);
const [deleteOpen, setDeleteOpen] = useState(false)
const [deleteItem, setDeleteItem] = useState<TTBAndamentoServicoInteface | null>(null)
const emptyForm: FormValues = {
tb_andamentoservico_id: 0,
descricao: "",
situacao: "I",
tipo: "",
usa_email: "",
};
useEffect(() => {
fetchTTBAndamentoServico();
}, []);
const handleSave = async (values: FormValues) => {
const saved = await saveTTBAndamentoServico(values);
addTTBAndamentoServico(saved);
setFormOpen(false);
}
if (!tTBAndamentosServicos) return <Loading type={2} />;
return (
<div>
<div className="flex">
<div className="w-64 flex-1">
<div className="text-4xl font-semibold mb-1">
Andamentos
</div>
<div className="text-base text-muted-foreground">
Gerenciamento de tipos de reconhecimentos
</div>
</div>
<div className="w-64 flex-1 text-end">
<Button onClick={() => { setEditingItem(emptyForm); setFormOpen(true) }} className="cursor-pointer">
<PlusIcon />Tipos
</Button>
</div>
</div>
<Card>
<CardContent>
<TTBAndamentoServicoTable
data={tTBAndamentosServicos}
onEdit={(item) => { setEditingItem(item); setFormOpen(true) }}
onDelete={(item) => { setDeleteItem(item); setDeleteOpen(true) }}
/>
</CardContent>
</Card>
<TTBAndamentoServicoForm
isOpen={formOpen}
onClose={() => setFormOpen(false)}
initialData={editingItem || emptyForm}
onSave={handleSave}
/>
< TTBAndamentoServicoAlert
isOpen={deleteOpen}
item={deleteItem}
onClose={() => setDeleteOpen(false)}
onConfirm={() => { setDeleteOpen(false) }}
/>
</div>
);
}

View file

@ -0,0 +1,38 @@
'use client'
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
import TTBAndamentoServicoInteface from "../../_interfaces/TTBAndamentoServicoInterface";
interface Props {
isOpen: boolean,
item?: TTBAndamentoServicoInteface | null,
onClose: () => void
onConfirm: () => void
}
export default function TTBAndamentoServicoAlert({ isOpen, item, onClose, onConfirm }: Props) {
return (
<div>
<AlertDialog open={isOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
#{item?.tb_andamentoservico_id} - {item?.descricao}
</AlertDialogTitle>
<AlertDialogDescription>
Esta ação não pode ser desfeita. Isso excluirá permanentemente o registro e seus dados dos nossos servidores.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={onClose} className="cursor-pointer">
Cancelar
</AlertDialogCancel>
<AlertDialogAction onClick={onConfirm} className="cursor-pointer">
Continuar
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
);
}

View file

@ -0,0 +1,150 @@
'use client'
import z from "zod";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Controller, useForm } from "react-hook-form";
import { TTBAndamentoServicoSchema } from "../../_schemas/TTBAndamentoServico";
import { tipoEnum } from "../../_interfaces/TTBAndamentoServicoInterface";
type FormValues = z.infer<typeof TTBAndamentoServicoSchema>
interface Props {
isOpen: boolean
onClose: () => void
initialData?: FormValues
onSave: (data: FormValues) => void
}
export default function TTBAndamentoServicoForm({ isOpen, onClose, initialData, onSave }: Props) {
const form = useForm<FormValues>({
resolver: undefined,
defaultValues: initialData || { tb_andamentoservico_id: 0, descricao: "", situacao: "I" }
})
const tipoOptions = Object.values(tipoEnum).map((value) => ({
value,
// Aqui você pode personalizar o label como quiser
label:
value === "C" ? "Cancelado" :
value === "E" ? "Estornado" :
value === "PT" ? "Protocolado" :
value === "PD" ? "Pedido" :
value === "NC" ? "Não Consta" :
"Outros"
}));
return (
<div>
<Dialog open={isOpen} onOpenChange={onClose}>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSave)}
className="space-y-1"
>
<DialogContent className="sm:max-w-[425px]">
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="space-y-8">
<DialogHeader>
<DialogTitle>Andamentos</DialogTitle>
<DialogDescription>
Controle de andamentos de atos
</DialogDescription>
</DialogHeader>
<FormField
control={form.control}
name="descricao"
render={({ field }) => (
<FormItem>
<FormLabel>
Descrição
</FormLabel>
<FormControl>
<Input type="text" placeholder="Digite a descrição" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="tipo"
render={({ field }) => (
<FormItem>
<FormLabel>
Tipo
</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl className="w-full">
<SelectTrigger>
<SelectValue placeholder="Escolha o tipo do andamento" />
</SelectTrigger>
</FormControl>
<SelectContent>
{tipoOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<div className="flex items-center space-x-2">
<Controller
name="situacao"
control={form.control}
render={({ field }) => (
<Checkbox
checked={field.value === "A"}
onCheckedChange={(checked) => field.onChange(checked ? "A" : "I")}
/>
)}
/>
<Label>
Ativo
</Label>
</div>
<div className="flex items-center space-x-2">
<Controller
name="usa_email"
control={form.control}
render={({ field }) => (
<Checkbox
checked={field.value === "A"}
onCheckedChange={(checked) => field.onChange(checked ? "A" : "I")}
/>
)}
/>
<Label>
Usar email
</Label>
</div>
<DialogFooter className="mb-0">
<DialogClose asChild>
<Button variant="outline" type="button" className="cursor-pointer">
Cancelar
</Button>
</DialogClose>
<Button type="submit" className="cursor-pointer">
Salvar
</Button>
</DialogFooter>
<input type="hidden" {...form.register("tb_andamentoservico_id")} />
</form>
</Form>
</DialogContent>
</form>
</Form>
</Dialog>
</div>
);
}

View file

@ -0,0 +1,79 @@
'use client'
import { Button } from "@/components/ui/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { EllipsisIcon, PencilIcon, Trash2Icon } from "lucide-react";
import TTBAndamentoServicoInteface from "../../_interfaces/TTBAndamentoServicoInterface";
interface Props {
data: TTBAndamentoServicoInteface[]
onEdit: (item: TTBAndamentoServicoInteface) => void
onDelete: (item: TTBAndamentoServicoInteface) => void
}
export default function TTBAndamentoServicoTable({ data, onEdit, onDelete }: Props) {
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>
#
</TableHead>
<TableHead>
Situação
</TableHead>
<TableHead>
Descrição
</TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map(
(item: TTBAndamentoServicoInteface) => (
<TableRow key={item.tb_andamentoservico_id} className="cursor-pointer">
<TableCell className="font-medium">
{item.tb_andamentoservico_id}
</TableCell>
<TableCell>
{item.situacao === 'A' ? (
<span className="bg-blue-100 text-blue-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded-sm dark:bg-blue-900 dark:text-blue-300">
Ativo
</span>
) : (
<span className="bg-yellow-100 text-yellow-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded-sm dark:bg-yellow-900 dark:text-yellow-300">
Inativo
</span>
)}
</TableCell>
<TableCell>
{item.descricao}
</TableCell>
<TableCell className="text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="cursor-pointer">
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuGroup>
<DropdownMenuItem className="cursor-pointer" onSelect={() => onEdit(item)}>
<PencilIcon /> Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="cursor-pointer" onSelect={() => onDelete(item)}>
<Trash2Icon /> Remover
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
);
}

View file

@ -0,0 +1,132 @@
export default async function TTBAndamentoServicoIndexData() {
return Promise.resolve({
data: [
{
tb_andamentoservico_id: 1,
descricao: "Recepção 1",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 2,
descricao: "Aguardando assinatura da Imobiliária",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 3,
descricao: "Esc. Impugnada (Falta Itbi, cert. ou documentos)",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 4,
descricao: "Aguardando Certtb_andamentoservico_idão de Registro",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 5,
descricao: "Conferencia",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 16,
descricao: "Mapa",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 6,
descricao: "Entregue",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 18,
descricao: "PROTOCOLADO",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 7,
descricao: "Protocolo",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 8,
descricao: "Cadastro",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 9,
descricao: "Registro/Espelho",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 10,
descricao: "Corrigtb_andamentoservico_ido Pronto",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 11,
descricao: "Aguardando Cliente",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 12,
descricao: "Cancelado a pedtb_andamentoservico_ido da parte",
status: "A",
tipo: "C"
},
{
tb_andamentoservico_id: 13,
descricao: "Cancelado por determinação judicial",
status: "A",
tipo: "C"
},
{
tb_andamentoservico_id: 14,
descricao: "Capas e Indicador",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 15,
descricao: "Falta assinatura",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 17,
descricao: "Impugnado",
status: "A",
tipo: "G"
},
{
tb_andamentoservico_id: 19,
descricao: "Entregre/sem registro",
status: "A",
tipo: "C"
},
{
tb_andamentoservico_id: 20,
descricao: "Corrigtb_andamentoservico_ido sem Registro",
status: "A",
tipo: "C"
},
{
tb_andamentoservico_id: 21,
descricao: "Cancelamento de ofício",
status: "A",
tipo: "C"
}
]
});
}

View file

@ -0,0 +1,16 @@
import { faker } from "@faker-js/faker";
export default async function name(andamentoServico: any) {
return Promise.resolve({
message: 'Dados salvos com sucesso',
data: {
tb_reconhecimentotipo_id: faker.number.int({ min: 1, max: 1000 }),
descricao: andamentoServico.tb_reconhecimentotipo_id + andamentoServico.descricao,
situacao: andamentoServico.situacao,
tipo: andamentoServico.tipo,
usa_email: andamentoServico.usa_email,
},
});
}

View file

@ -0,0 +1,33 @@
'use client'
import { useResponse } from "@/app/_response/ResponseContext"
import { useState } from "react";
import TTBAndamentoServicoInteface from "../../_interfaces/TTBAndamentoServicoInterface";
import TTBAndamentoServicoIndexData from "../../_data/TTBAndamentoServico/TTBAndamentoServicoIndexData";
export const useTTBAndamentoServicoReadHook = () => {
const { setResponse } = useResponse();
const [tTBAndamentosServicos, setTTBAndamentosServicos] = useState<TTBAndamentoServicoInteface[] | null>(null);
const fetchTTBAndamentoServico = async () => {
const response = await TTBAndamentoServicoIndexData();
setTTBAndamentosServicos(response.data);
// Define os dados do componente de resposta (toast, modal, etc)
setResponse(response);
}
function addTTBAndamentoServico(tTTBAndamentoServico: TTBAndamentoServicoInteface) {
setTTBAndamentosServicos(prev => [...(prev || []), tTTBAndamentoServico]);
}
return { tTBAndamentosServicos, fetchTTBAndamentoServico, addTTBAndamentoServico }
}

View file

@ -0,0 +1,29 @@
'use client'
import { useResponse } from "@/app/_response/ResponseContext"
import { use, useState } from "react";
import TTBAndamentoServicoInteface from "../../_interfaces/TTBAndamentoServicoInterface";
import TTBReconhecimentoTipoSaveData from "../../_data/TTBReconhecimentoTipo/TTBReconhecimentoTipoSaveData";
export const useTTBAndamentoServicoSaveHook = () => {
const { setResponse } = useResponse();
const [tTBAndamentoServico, setTTBAndamentoServico] = useState<TTBAndamentoServicoInteface>();
const saveTTBAndamentoServico = async (form: TTBAndamentoServicoInteface) => {
const response = await TTBReconhecimentoTipoSaveData(form);
setTTBAndamentoServico(response.data);
setResponse(response);
// Retorna os valores de forma imediata
return response.data;
}
return { tTBAndamentoServico, saveTTBAndamentoServico }
}

View file

@ -0,0 +1,15 @@
export default interface TTBAndamentoServicoInteface {
tb_andamentoservico_id: number,
descricao: null | string,
situacao: null | string,
tipo: null | tipoEnum,
}
export enum tipoEnum {
CANCELADO = "C",
ESTORNADO = "E",
PROTOCOLADO = "PT",
PEDIDO = "PD",
NAO_CONSTA = "NC",
OUTROS = "O",
}

View file

@ -0,0 +1,11 @@
import { z } from 'zod';
export const TTBAndamentoServicoSchema = z.object({
tb_andamentoservico_id: z.number(),
descricao: z.string(),
situacao: z.string(),
tipo: z.string(),
usa_email: z.string()
});

View file

@ -6,8 +6,8 @@
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--font-sans: Inter, sans-serif;
--font-mono: JetBrains Mono, monospace;
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
@ -41,75 +41,142 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--font-serif: Source Serif 4, serif;
--radius: 0.375rem;
--tracking-tighter: calc(var(--tracking-normal) - 0.05em);
--tracking-tight: calc(var(--tracking-normal) - 0.025em);
--tracking-wide: calc(var(--tracking-normal) + 0.025em);
--tracking-wider: calc(var(--tracking-normal) + 0.05em);
--tracking-widest: calc(var(--tracking-normal) + 0.1em);
--tracking-normal: var(--tracking-normal);
--shadow-2xl: var(--shadow-2xl);
--shadow-xl: var(--shadow-xl);
--shadow-lg: var(--shadow-lg);
--shadow-md: var(--shadow-md);
--shadow: var(--shadow);
--shadow-sm: var(--shadow-sm);
--shadow-xs: var(--shadow-xs);
--shadow-2xs: var(--shadow-2xs);
--spacing: var(--spacing);
--letter-spacing: var(--letter-spacing);
--shadow-offset-y: var(--shadow-offset-y);
--shadow-offset-x: var(--shadow-offset-x);
--shadow-spread: var(--shadow-spread);
--shadow-blur: var(--shadow-blur);
--shadow-opacity: var(--shadow-opacity);
--color-shadow-color: var(--shadow-color);
--color-destructive-foreground: var(--destructive-foreground);
}
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--radius: 0.375rem;
--background: oklch(1.0000 0 0);
--foreground: oklch(0.2686 0 0);
--card: oklch(1.0000 0 0);
--card-foreground: oklch(0.2686 0 0);
--popover: oklch(1.0000 0 0);
--popover-foreground: oklch(0.2686 0 0);
--primary: oklch(0.7686 0.1647 70.0804);
--primary-foreground: oklch(0 0 0);
--secondary: oklch(0.9670 0.0029 264.5419);
--secondary-foreground: oklch(0.4461 0.0263 256.8018);
--muted: oklch(0.9846 0.0017 247.8389);
--muted-foreground: oklch(0.5510 0.0234 264.3637);
--accent: oklch(0.9869 0.0214 95.2774);
--accent-foreground: oklch(0.4732 0.1247 46.2007);
--destructive: oklch(0.6368 0.2078 25.3313);
--border: oklch(0.9276 0.0058 264.5313);
--input: oklch(0.9276 0.0058 264.5313);
--ring: oklch(0.7686 0.1647 70.0804);
--chart-1: oklch(0.7686 0.1647 70.0804);
--chart-2: oklch(0.6658 0.1574 58.3183);
--chart-3: oklch(0.5553 0.1455 48.9975);
--chart-4: oklch(0.4732 0.1247 46.2007);
--chart-5: oklch(0.4137 0.1054 45.9038);
--sidebar: oklch(0.9846 0.0017 247.8389);
--sidebar-foreground: oklch(0.2686 0 0);
--sidebar-primary: oklch(0.7686 0.1647 70.0804);
--sidebar-primary-foreground: oklch(1.0000 0 0);
--sidebar-accent: oklch(0.9869 0.0214 95.2774);
--sidebar-accent-foreground: oklch(0.4732 0.1247 46.2007);
--sidebar-border: oklch(0.9276 0.0058 264.5313);
--sidebar-ring: oklch(0.7686 0.1647 70.0804);
--destructive-foreground: oklch(1.0000 0 0);
--font-sans: Inter, sans-serif;
--font-serif: Source Serif 4, serif;
--font-mono: JetBrains Mono, monospace;
--shadow-color: hsl(0 0% 0%);
--shadow-opacity: 0.1;
--shadow-blur: 8px;
--shadow-spread: -1px;
--shadow-offset-x: 0px;
--shadow-offset-y: 4px;
--letter-spacing: 0em;
--spacing: 0.25rem;
--shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05);
--shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05);
--shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10);
--shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10);
--shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10);
--shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10);
--shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10);
--shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.25);
--tracking-normal: 0em;
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
--background: oklch(0.2046 0 0);
--foreground: oklch(0.9219 0 0);
--card: oklch(0.2686 0 0);
--card-foreground: oklch(0.9219 0 0);
--popover: oklch(0.2686 0 0);
--popover-foreground: oklch(0.9219 0 0);
--primary: oklch(0.7686 0.1647 70.0804);
--primary-foreground: oklch(0 0 0);
--secondary: oklch(0.2686 0 0);
--secondary-foreground: oklch(0.9219 0 0);
--muted: oklch(0.2686 0 0);
--muted-foreground: oklch(0.7155 0 0);
--accent: oklch(0.4732 0.1247 46.2007);
--accent-foreground: oklch(0.9243 0.1151 95.7459);
--destructive: oklch(0.6368 0.2078 25.3313);
--border: oklch(0.3715 0 0);
--input: oklch(0.3715 0 0);
--ring: oklch(0.7686 0.1647 70.0804);
--chart-1: oklch(0.8369 0.1644 84.4286);
--chart-2: oklch(0.6658 0.1574 58.3183);
--chart-3: oklch(0.4732 0.1247 46.2007);
--chart-4: oklch(0.5553 0.1455 48.9975);
--chart-5: oklch(0.4732 0.1247 46.2007);
--sidebar: oklch(0.1684 0 0);
--sidebar-foreground: oklch(0.9219 0 0);
--sidebar-primary: oklch(0.7686 0.1647 70.0804);
--sidebar-primary-foreground: oklch(1.0000 0 0);
--sidebar-accent: oklch(0.4732 0.1247 46.2007);
--sidebar-accent-foreground: oklch(0.9243 0.1151 95.7459);
--sidebar-border: oklch(0.3715 0 0);
--sidebar-ring: oklch(0.7686 0.1647 70.0804);
--destructive-foreground: oklch(1.0000 0 0);
--radius: 0.375rem;
--font-sans: Inter, sans-serif;
--font-serif: Source Serif 4, serif;
--font-mono: JetBrains Mono, monospace;
--shadow-color: hsl(0 0% 0%);
--shadow-opacity: 0.1;
--shadow-blur: 8px;
--shadow-spread: -1px;
--shadow-offset-x: 0px;
--shadow-offset-y: 4px;
--letter-spacing: 0em;
--spacing: 0.25rem;
--shadow-2xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05);
--shadow-xs: 0px 4px 8px -1px hsl(0 0% 0% / 0.05);
--shadow-sm: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10);
--shadow: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10);
--shadow-md: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10);
--shadow-lg: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10);
--shadow-xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10);
--shadow-2xl: 0px 4px 8px -1px hsl(0 0% 0% / 0.25);
}
@layer base {
@ -118,8 +185,9 @@
}
body {
@apply bg-background text-foreground;
letter-spacing: var(--tracking-normal);
}
.bg-brand{
background-color: #1A292F;
}
}
}

View file

@ -72,6 +72,10 @@ const data = {
title: "Reconhecimentos",
url: "/cadastros/reconhecimentos/",
},
{
title: "Andamentos",
url: "/cadastros/andamentos/",
},
],
},
{

View file

@ -0,0 +1,48 @@
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
function Popover({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
return <PopoverPrimitive.Root data-slot="popover" {...props} />
}
function PopoverTrigger({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
}
function PopoverContent({
className,
align = "center",
sideOffset = 4,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
data-slot="popover-content"
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
)
}
function PopoverAnchor({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
}
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }

View file

@ -0,0 +1,185 @@
"use client"
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Select({
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
return <SelectPrimitive.Root data-slot="select" {...props} />
}
function SelectGroup({
...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
return <SelectPrimitive.Group data-slot="select-group" {...props} />
}
function SelectValue({
...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
return <SelectPrimitive.Value data-slot="select-value" {...props} />
}
function SelectTrigger({
className,
size = "default",
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default"
}) {
return (
<SelectPrimitive.Trigger
data-slot="select-trigger"
data-size={size}
className={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDownIcon className="size-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
)
}
function SelectContent({
className,
children,
position = "popper",
...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
return (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
data-slot="select-content"
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
)
}
function SelectLabel({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
return (
<SelectPrimitive.Label
data-slot="select-label"
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
{...props}
/>
)
}
function SelectItem({
className,
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
return (
<SelectPrimitive.Item
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className
)}
{...props}
>
<span className="absolute right-2 flex size-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<CheckIcon className="size-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
)
}
function SelectSeparator({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
return (
<SelectPrimitive.Separator
data-slot="select-separator"
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
{...props}
/>
)
}
function SelectScrollUpButton({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
return (
<SelectPrimitive.ScrollUpButton
data-slot="select-scroll-up-button"
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronUpIcon className="size-4" />
</SelectPrimitive.ScrollUpButton>
)
}
function SelectScrollDownButton({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
return (
<SelectPrimitive.ScrollDownButton
data-slot="select-scroll-down-button"
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronDownIcon className="size-4" />
</SelectPrimitive.ScrollDownButton>
)
}
export {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
}