Compare commits
15 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a4dc8cb2f4 | |||
| fb57ae2701 | |||
| fb6982da6b | |||
| ed91680655 | |||
| 07ae4170fc | |||
| c7fe1b259f | |||
| ee3bb84269 | |||
| fc729c6b77 | |||
| 8e4c9db75a | |||
| 381301f6e0 | |||
| b522cc0033 | |||
| f96dcd87cb | |||
| 8c4970767b | |||
| a1d7e9ff28 | |||
| 55a16da28f |
75 changed files with 15712 additions and 857 deletions
7
.dockerignore
Normal file
7
.dockerignore
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
README.md
|
||||||
|
.next
|
||||||
|
.git
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -39,3 +39,5 @@ yarn-error.log*
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
|
/src/generated/prisma
|
||||||
|
|
|
||||||
4
.prettierrc
Normal file
4
.prettierrc
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false
|
||||||
|
}
|
||||||
50
Dockerfile
50
Dockerfile
|
|
@ -1,38 +1,44 @@
|
||||||
# Fase 1: Build
|
FROM node:22-alpine AS base
|
||||||
FROM node:18-alpine AS builder
|
|
||||||
|
RUN apk add --no-cache libc6-compat openssl
|
||||||
|
|
||||||
# Imposta la directory di lavoro
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copia i file di configurazione e dipendenze
|
# --- STAGE 1 ---
|
||||||
COPY package.json pnpm-lock.yaml* ./
|
FROM base AS deps
|
||||||
|
COPY package.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
# Installa le dipendenze
|
# --- STAGE 2 ---
|
||||||
RUN npm install --force
|
FROM base AS builder
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
# Copia il resto dei file dell'app
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build dell'app Next.js
|
RUN npx prisma generate
|
||||||
|
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Fase 2: Runtime (immagine più leggera)
|
# --- STAGE 3 ---
|
||||||
FROM node:18-alpine AS runner
|
FROM base AS runner
|
||||||
|
|
||||||
# Imposta la directory di lavoro
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Imposta variabile d'ambiente per produzione
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
# Copia solo ciò che serve per l'esecuzione
|
|
||||||
COPY --from=builder /app/public ./public
|
COPY --from=builder /app/public ./public
|
||||||
COPY --from=builder /app/.next ./.next
|
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
|
||||||
COPY --from=builder /app/package.json ./package.json
|
|
||||||
|
|
||||||
# Porta esposta (3000 di default per Next.js)
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Comando di avvio
|
CMD ["node", "server.js"]
|
||||||
CMD ["npm", "start"]
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,27 @@
|
||||||
services:
|
services:
|
||||||
app:
|
webapp:
|
||||||
container_name: dashregistratori
|
build: .
|
||||||
build:
|
container_name: dash-registratori
|
||||||
context: .
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 3001:3000
|
- "3000:3000"
|
||||||
dns: 1.1.1.1
|
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: development
|
DATABASE_URL: "postgresql://postgres:postgres@db:5432/db?schema=public"
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:alpine
|
||||||
|
container_name: db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: db
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,14 @@ import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
|
|
||||||
|
typescript: {
|
||||||
|
ignoreBuildErrors: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
eslint: {
|
||||||
|
ignoreDuringBuilds: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
1998
package-lock.json
generated
1998
package-lock.json
generated
File diff suppressed because it is too large
Load diff
11
package.json
11
package.json
|
|
@ -8,23 +8,29 @@
|
||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@prisma/client": "^6.18.0",
|
||||||
"@radix-ui/react-avatar": "^1.1.10",
|
"@radix-ui/react-avatar": "^1.1.10",
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.15",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"@radix-ui/react-label": "^2.1.7",
|
||||||
|
"@radix-ui/react-popover": "^1.1.15",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"cmdk": "^1.1.1",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"leaflet-defaulticon-compatibility": "^0.1.2",
|
"leaflet-defaulticon-compatibility": "^0.1.2",
|
||||||
"lucide-react": "^0.546.0",
|
"lucide-react": "^0.546.0",
|
||||||
"next": "15.5.5",
|
"next": "15.5.5",
|
||||||
|
"prisma": "^6.17.1",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
|
"react-day-picker": "^9.11.1",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-leaflet": "^5.0.0",
|
"react-leaflet": "^5.0.0",
|
||||||
"tailwind-merge": "^3.3.1"
|
"tailwind-merge": "^3.3.1"
|
||||||
|
|
@ -33,8 +39,9 @@
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"@types/leaflet": "^1.9.21",
|
"@types/leaflet": "^1.9.21",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19.2.6",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
|
"prisma": "^6.18.0",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Intervento" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"id_registratore" INTEGER NOT NULL,
|
||||||
|
"data" DATE NOT NULL,
|
||||||
|
"lavoro" TEXT NOT NULL,
|
||||||
|
"fattura" BOOLEAN NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Intervento_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Registratore" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"id_cliente" INTEGER NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Registratore_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Cliente" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"ragione_sociale" VARCHAR(255) NOT NULL,
|
||||||
|
"email" VARCHAR(255) NOT NULL,
|
||||||
|
"partita_iva" VARCHAR(255) NOT NULL,
|
||||||
|
"telefono" VARCHAR(255) NOT NULL,
|
||||||
|
"sede" VARCHAR(255) NOT NULL,
|
||||||
|
"sede_url" VARCHAR(255) NOT NULL,
|
||||||
|
"contratto" VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Cliente_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Cliente_ragione_sociale_key" ON "Cliente"("ragione_sociale");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Intervento" ADD CONSTRAINT "Intervento_id_registratore_fkey" FOREIGN KEY ("id_registratore") REFERENCES "Registratore"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Registratore" ADD CONSTRAINT "Registratore_id_cliente_fkey" FOREIGN KEY ("id_cliente") REFERENCES "Cliente"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
5
prisma/migrations/20251104102414_002/migration.sql
Normal file
5
prisma/migrations/20251104102414_002/migration.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Registratore" ADD COLUMN "data_acquisto" DATE,
|
||||||
|
ADD COLUMN "prossima_verifica" DATE,
|
||||||
|
ADD COLUMN "seriale" VARCHAR(255),
|
||||||
|
ADD COLUMN "ultima_verifica" DATE;
|
||||||
21
prisma/migrations/20251111124743_002/migration.sql
Normal file
21
prisma/migrations/20251111124743_002/migration.sql
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- The primary key for the `Registratore` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "public"."Intervento" DROP CONSTRAINT "Intervento_id_registratore_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Intervento" ALTER COLUMN "id_registratore" SET DATA TYPE TEXT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Registratore" DROP CONSTRAINT "Registratore_pkey",
|
||||||
|
ALTER COLUMN "id" DROP DEFAULT,
|
||||||
|
ALTER COLUMN "id" SET DATA TYPE TEXT,
|
||||||
|
ADD CONSTRAINT "Registratore_pkey" PRIMARY KEY ("id");
|
||||||
|
DROP SEQUENCE "Registratore_id_seq";
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Intervento" ADD CONSTRAINT "Intervento_id_registratore_fkey" FOREIGN KEY ("id_registratore") REFERENCES "Registratore"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
8
prisma/migrations/20251111124854_003/migration.sql
Normal file
8
prisma/migrations/20251111124854_003/migration.sql
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `seriale` on the `Registratore` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Registratore" DROP COLUMN "seriale";
|
||||||
5
prisma/migrations/20251111125913_004/migration.sql
Normal file
5
prisma/migrations/20251111125913_004/migration.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "Modello" AS ENUM ('FORM100', 'FORM200', 'FORM500');
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Registratore" ADD COLUMN "modello" "Modello";
|
||||||
2
prisma/migrations/20251111130324_005/migration.sql
Normal file
2
prisma/migrations/20251111130324_005/migration.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterEnum
|
||||||
|
ALTER TYPE "Modello" ADD VALUE 'FORM200PLUS';
|
||||||
2
prisma/migrations/20251111140337_006/migration.sql
Normal file
2
prisma/migrations/20251111140337_006/migration.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Cliente" ADD COLUMN "coordinate" DOUBLE PRECISION[];
|
||||||
10
prisma/migrations/20251111161412_007/migration.sql
Normal file
10
prisma/migrations/20251111161412_007/migration.sql
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `coordinate` on the `Cliente` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Cliente" DROP COLUMN "coordinate",
|
||||||
|
ADD COLUMN "lat" DOUBLE PRECISION,
|
||||||
|
ADD COLUMN "lon" DOUBLE PRECISION;
|
||||||
10
prisma/migrations/20251111164927_008/migration.sql
Normal file
10
prisma/migrations/20251111164927_008/migration.sql
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Made the column `lat` on table `Cliente` required. This step will fail if there are existing NULL values in that column.
|
||||||
|
- Made the column `lon` on table `Cliente` required. This step will fail if there are existing NULL values in that column.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Cliente" ALTER COLUMN "lat" SET NOT NULL,
|
||||||
|
ALTER COLUMN "lon" SET NOT NULL;
|
||||||
2
prisma/migrations/20251120094202_009/migration.sql
Normal file
2
prisma/migrations/20251120094202_009/migration.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Cliente" ALTER COLUMN "sede_url" SET DATA TYPE TEXT;
|
||||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "postgresql"
|
||||||
50
prisma/schema.prisma
Normal file
50
prisma/schema.prisma
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
output = "../src/generated/prisma"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Modello {
|
||||||
|
FORM100
|
||||||
|
FORM200
|
||||||
|
FORM200PLUS
|
||||||
|
FORM500
|
||||||
|
}
|
||||||
|
|
||||||
|
model Intervento {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
id_registratore String
|
||||||
|
data DateTime @db.Date
|
||||||
|
lavoro String @db.Text
|
||||||
|
fattura Boolean @db.Boolean
|
||||||
|
registratore Registratore @relation(fields: [id_registratore], references: [id])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Registratore {
|
||||||
|
id String @id
|
||||||
|
id_cliente Int
|
||||||
|
modello Modello?
|
||||||
|
data_acquisto DateTime? @db.Date
|
||||||
|
ultima_verifica DateTime? @db.Date
|
||||||
|
prossima_verifica DateTime? @db.Date
|
||||||
|
cliente Cliente @relation(fields: [id_cliente], references: [id])
|
||||||
|
interventi Intervento[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Cliente {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
ragione_sociale String @unique @db.VarChar(255)
|
||||||
|
email String @db.VarChar(255)
|
||||||
|
partita_iva String @db.VarChar(255)
|
||||||
|
telefono String @db.VarChar(255)
|
||||||
|
sede String @db.VarChar(255)
|
||||||
|
sede_url String
|
||||||
|
contratto String @db.VarChar(255)
|
||||||
|
lat Float
|
||||||
|
lon Float
|
||||||
|
registratori Registratore[]
|
||||||
|
}
|
||||||
24
src/app/api/clienti/[id]/route.ts
Normal file
24
src/app/api/clienti/[id]/route.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { PrismaClient } from "@/generated/prisma";
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: Promise<{ id: string }> },
|
||||||
|
) {
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
const { id } = await params;
|
||||||
|
|
||||||
|
const cliente = await prisma.cliente.findUnique({
|
||||||
|
where: {
|
||||||
|
id: Number(id),
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
registratori: {
|
||||||
|
include: {
|
||||||
|
interventi: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json({ cliente });
|
||||||
|
}
|
||||||
66
src/app/api/clienti/route.ts
Normal file
66
src/app/api/clienti/route.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { Cliente, Modello, PrismaClient, Prisma } from "@/generated/prisma";
|
||||||
|
|
||||||
|
export async function GET(request: Request) {
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
const clients = await prisma.cliente.findMany({
|
||||||
|
include: {
|
||||||
|
registratori: {
|
||||||
|
include: {
|
||||||
|
interventi: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json({ clients });
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractLatLonFromGmapsUrl(url: string) {
|
||||||
|
// Usa una regex per cercare la latitudine e la longitudine nell'URL
|
||||||
|
const regex = /@(-?\d+\.\d+),(-?\d+\.\d+)/;
|
||||||
|
const match = url.match(regex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const lat = parseFloat(match[1]);
|
||||||
|
const lon = parseFloat(match[2]);
|
||||||
|
return [lat, lon]; // Restituisce un array con latitudine e longitudine
|
||||||
|
} else {
|
||||||
|
return [0, 0]; // Se non trova latitudine e longitudine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
const data = await request.json();
|
||||||
|
const [lat, lon] = extractLatLonFromGmapsUrl(data.sede_url);
|
||||||
|
|
||||||
|
const cliente = {
|
||||||
|
contratto: data.contratto,
|
||||||
|
email: data.email,
|
||||||
|
partita_iva: data.partita_iva,
|
||||||
|
ragione_sociale: data.ragione_sociale,
|
||||||
|
sede: data.sede,
|
||||||
|
sede_url: data.sede_url,
|
||||||
|
telefono: data.telefono,
|
||||||
|
lat: lat,
|
||||||
|
lon: lon,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await prisma.cliente.create({
|
||||||
|
data: cliente,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
|
if (e.code == "P2002") {
|
||||||
|
return Response.json(
|
||||||
|
{ message: "Il cliente esista già" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({ message: "Cliente creato" });
|
||||||
|
}
|
||||||
17
src/app/api/interventi/route.ts
Normal file
17
src/app/api/interventi/route.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { PrismaClient } from "@/generated/prisma";
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
const data = await request.json();
|
||||||
|
|
||||||
|
await prisma.intervento.create({
|
||||||
|
data: {
|
||||||
|
id_registratore: data.id,
|
||||||
|
data: new Date(data.data),
|
||||||
|
lavoro: data.lavoro,
|
||||||
|
fattura: data.fattura,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json({ message: "fatto bastardo" });
|
||||||
|
}
|
||||||
17
src/app/api/registratori/interventi/[id]/route.ts
Normal file
17
src/app/api/registratori/interventi/[id]/route.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { PrismaClient } from "@/generated/prisma";
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
request: Request,
|
||||||
|
{ params }: { params: Promise<{ id: string }> },
|
||||||
|
) {
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
const { id }: { id: string } = await params;
|
||||||
|
|
||||||
|
const cliente = await prisma.intervento.findMany({
|
||||||
|
where: {
|
||||||
|
id_registratore: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json({ cliente });
|
||||||
|
}
|
||||||
24
src/app/api/registratori/route.ts
Normal file
24
src/app/api/registratori/route.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Modello, PrismaClient } from "@/generated/prisma";
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
const data = await request.json();
|
||||||
|
|
||||||
|
await prisma.registratore.create({
|
||||||
|
data: {
|
||||||
|
id: data.seriale,
|
||||||
|
id_cliente: data.id,
|
||||||
|
data_acquisto: new Date(data.data),
|
||||||
|
modello:
|
||||||
|
data.modello === "FORM100"
|
||||||
|
? Modello.FORM100
|
||||||
|
: data.modello === "FORM200"
|
||||||
|
? Modello.FORM200
|
||||||
|
: data.modello === "FORM200PLUS"
|
||||||
|
? Modello.FORM200PLUS
|
||||||
|
: Modello.FORM500,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return Response.json({ message: "fatto bastardo" });
|
||||||
|
}
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { AppSidebar } from "@/components/app-sidebar";
|
|
||||||
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
|
||||||
import ClientCard from "@/components/client-card";
|
|
||||||
import DeviceCard from "@/components/device-card";
|
|
||||||
import { useSearchParams, useParams } from "next/navigation";
|
|
||||||
|
|
||||||
export default function Page() {
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const { client } = useParams();
|
|
||||||
|
|
||||||
const clienti = [
|
|
||||||
{
|
|
||||||
name: "Savoldi Ettore",
|
|
||||||
email: "savoldi.ettore@gmail.com",
|
|
||||||
ragione_sociale: "Acconciature Uomo",
|
|
||||||
p_iva: "13407520172",
|
|
||||||
telefono: "0301547854",
|
|
||||||
sede: "Via Umberto I 60/T, Flero (BS)",
|
|
||||||
sede_url: "https://maps.app.goo.gl/9uNbw2a62ZCCjkQc7",
|
|
||||||
contratto: "https://google.com",
|
|
||||||
registratori: [
|
|
||||||
{
|
|
||||||
seriale: "80E100548745",
|
|
||||||
acquisto: "15/10/2019",
|
|
||||||
ultima_verifica: "15/10/2025",
|
|
||||||
prossima_verifica: "15/10/2026",
|
|
||||||
interventi: [
|
|
||||||
{
|
|
||||||
id: "0001",
|
|
||||||
data: "15/10/2025",
|
|
||||||
lavoro: "VERIFICA FISCALE - AGGIORNAMENTO FIRMWARE",
|
|
||||||
fattura: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "0002",
|
|
||||||
data: "28/05/2025",
|
|
||||||
lavoro: "SOSTITUZIONE DGFE",
|
|
||||||
fattura: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "0003",
|
|
||||||
data: "08/10/2024",
|
|
||||||
lavoro: "VERIFICA FISCALE",
|
|
||||||
fattura: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Forno Tagliaferri",
|
|
||||||
email: "info@tagliaferri.it",
|
|
||||||
ragione_sociale: "Forno Tagliaferri",
|
|
||||||
p_iva: "12901475639",
|
|
||||||
telefono: "0309183573",
|
|
||||||
sede: "Via Corso dei Martiri 11, Brescia (BS)",
|
|
||||||
sede_url: "https://maps.app.goo.gl/9uNbw2a62ZCCjkQc7",
|
|
||||||
contratto: "https://google.com",
|
|
||||||
registratori: [
|
|
||||||
{
|
|
||||||
seriale: "80E1002587545",
|
|
||||||
acquisto: "24/02/2020",
|
|
||||||
ultima_verifica: "24/02/2025",
|
|
||||||
prossima_verifica: "24/02/2026",
|
|
||||||
interventi: [
|
|
||||||
{
|
|
||||||
id: "0004",
|
|
||||||
data: "24/02/2025",
|
|
||||||
lavoro: "VERIFICA FISCALE",
|
|
||||||
fattura: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "0005",
|
|
||||||
data: "06/04/2025",
|
|
||||||
lavoro: "SOSTITUZIONE DGFE",
|
|
||||||
fattura: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "0006",
|
|
||||||
data: "24/02/2025",
|
|
||||||
lavoro: "VERIFICA FISCALE - AGGIORNAMENTO FIRMWARE",
|
|
||||||
fattura: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SidebarProvider
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"--sidebar-width": "350px",
|
|
||||||
} as React.CSSProperties
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<AppSidebar />
|
|
||||||
<SidebarInset>
|
|
||||||
|
|
||||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
|
||||||
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
|
||||||
<ClientCard client={client} />
|
|
||||||
<DeviceCard clienti={clienti} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SidebarInset>
|
|
||||||
</SidebarProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
50
src/app/client/page.tsx
Normal file
50
src/app/client/page.tsx
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import ClientCard from "@/components/client-card";
|
||||||
|
import DeviceCard from "@/components/device-card";
|
||||||
|
import { Cliente, Intervento, Registratore } from "@/generated/prisma";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { useEffect, useState, Suspense } from "react";
|
||||||
|
|
||||||
|
function ClientComponent() {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const id = searchParams.get("client");
|
||||||
|
const [cliente, setCliente] = useState<Cliente>();
|
||||||
|
const [registratori, setRegistratori] = useState<Array<Registratore>>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getCliente() {
|
||||||
|
const req = await fetch(`/api/clienti/${id}`);
|
||||||
|
const data = await req.json();
|
||||||
|
setCliente(data.cliente);
|
||||||
|
setRegistratori(data.cliente.registratori);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCliente();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||||
|
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||||
|
{cliente ? <ClientCard cliente={cliente} /> : <></>}
|
||||||
|
{registratori ? (
|
||||||
|
registratori.map((registratore) => (
|
||||||
|
<DeviceCard registratore={registratore} key={registratore.id} />
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<Suspense
|
||||||
|
fallback={<div className="p-8 text-center">Caricamento cliente...</div>}
|
||||||
|
>
|
||||||
|
<ClientComponent />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -28,25 +28,13 @@ import {
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { useSearchParams } from "next/navigation";
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<SidebarProvider
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"--sidebar-width": "350px",
|
|
||||||
} as React.CSSProperties
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<AppSidebar />
|
|
||||||
<SidebarInset>
|
|
||||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||||
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:shadow-xs lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||||
THIS IS A DASHBOARD, AND I DON'T KNOW WHAT TO PUT IN IT
|
THIS IS A DASHBOARD, AND I DON'T KNOW WHAT TO PUT IN IT
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SidebarInset>
|
|
||||||
</SidebarProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Geist, Geist_Mono } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
||||||
|
import { AppSidebar } from "@/components/app-sidebar";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
|
|
@ -27,7 +29,18 @@ export default function RootLayout({
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||||
>
|
>
|
||||||
|
<SidebarProvider
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--sidebar-width": "350px",
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<AppSidebar />
|
||||||
|
<SidebarInset>
|
||||||
{children}
|
{children}
|
||||||
|
</SidebarInset>
|
||||||
|
</SidebarProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,42 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { AppSidebar } from "@/components/app-sidebar";
|
|
||||||
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { useEffect, useState, Suspense } from "react";
|
||||||
|
import { Cliente } from "@/generated/prisma";
|
||||||
|
|
||||||
export default function Page() {
|
function MapContent() {
|
||||||
const MapWithNoSSR = dynamic(() => import("../../components/map"), {
|
const MapWithNoSSR = dynamic(() => import("../../components/map"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
return (
|
const [clienti, setClienti] = useState<Array<Cliente>>();
|
||||||
<SidebarProvider
|
const searchParams = useSearchParams();
|
||||||
style={
|
const client = searchParams.get("client");
|
||||||
{
|
|
||||||
"--sidebar-width": "350px",
|
useEffect(() => {
|
||||||
} as React.CSSProperties
|
async function getClienti() {
|
||||||
|
const req = await fetch(`/api/clienti`);
|
||||||
|
const data = await req.json();
|
||||||
|
setClienti(data.clients);
|
||||||
}
|
}
|
||||||
>
|
|
||||||
<AppSidebar />
|
getClienti();
|
||||||
<SidebarInset>
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
<div id="map" className="w-full h-full border rounded-md m-0 p-0">
|
<div id="map" className="w-full h-full border rounded-md m-0 p-0">
|
||||||
<MapWithNoSSR />
|
<MapWithNoSSR
|
||||||
|
highlight={clienti?.find((cliente) => cliente.id == +(client || -1))}
|
||||||
|
clienti={clienti || undefined}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SidebarInset>
|
);
|
||||||
</SidebarProvider>
|
}
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<div>Caricamento mappa...</div>}>
|
||||||
|
<MapContent />
|
||||||
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
161
src/components/add-client.tsx
Normal file
161
src/components/add-client.tsx
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const AddClientDialog = () => {
|
||||||
|
const [wait, setWait] = useState(false);
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [nome, setNome] = useState("");
|
||||||
|
const [ragione_sociale, setRagione_sociale] = useState("");
|
||||||
|
const [partita_iva, setPartita_iva] = useState("");
|
||||||
|
const [telefono, setTelefono] = useState("");
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [sede, setSede] = useState("");
|
||||||
|
const [sede_url, setSede_url] = useState("");
|
||||||
|
const [contratto, setContratto] = useState("");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
|
<form className="z-10">
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline">Aggiungi cliente</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Aggiungi cliente</DialogTitle>
|
||||||
|
{/*<DialogDescription>
|
||||||
|
Make changes to your profile here. Click save when you're
|
||||||
|
done.
|
||||||
|
</DialogDescription>*/}
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="name">Nome</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
placeholder="Nome"
|
||||||
|
onChange={(e) => setNome(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="ragione_sociale">Ragione sociale</Label>
|
||||||
|
<Input
|
||||||
|
id="ragione_sociale"
|
||||||
|
name="ragione_sociale"
|
||||||
|
placeholder="Ragione sociale"
|
||||||
|
onChange={(e) => setRagione_sociale(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="p_iva">Partita IVA</Label>
|
||||||
|
<Input
|
||||||
|
id="p_iva"
|
||||||
|
name="p_iva"
|
||||||
|
placeholder="Partita IVA"
|
||||||
|
onChange={(e) => setPartita_iva(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="tel">Numero di telefono</Label>
|
||||||
|
<Input
|
||||||
|
id="tel"
|
||||||
|
name="tel"
|
||||||
|
placeholder="Numero di telefono"
|
||||||
|
onChange={(e) => setTelefono(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="tel">Indirizzo e-mail</Label>
|
||||||
|
<Input
|
||||||
|
id="mail"
|
||||||
|
name="mail"
|
||||||
|
placeholder="Indirizzo e-mail"
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="sede_desc">Sede (descrizione)</Label>
|
||||||
|
<Input
|
||||||
|
id="sede_desc"
|
||||||
|
name="sede_desc"
|
||||||
|
placeholder="Sede - descrizione"
|
||||||
|
onChange={(e) => setSede(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="sede_link">Sede (link GMaps)</Label>
|
||||||
|
<Input
|
||||||
|
id="sede_link"
|
||||||
|
name="sede_link"
|
||||||
|
placeholder="Sede - link"
|
||||||
|
onChange={(e) => setSede_url(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="contratto">Contratto (link)</Label>
|
||||||
|
<Input
|
||||||
|
id="contratto"
|
||||||
|
name="contratto"
|
||||||
|
placeholder="Contratto - link"
|
||||||
|
onChange={(e) => setContratto(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button variant="outline">Cancella</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
setWait(true);
|
||||||
|
|
||||||
|
const res = await fetch("/api/clienti", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
nome: nome,
|
||||||
|
ragione_sociale: ragione_sociale,
|
||||||
|
partita_iva: partita_iva,
|
||||||
|
telefono: telefono,
|
||||||
|
email: email,
|
||||||
|
sede: sede,
|
||||||
|
sede_url: sede_url,
|
||||||
|
contratto: contratto,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
setWait(false);
|
||||||
|
|
||||||
|
if (res.status == 200) {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="submit"
|
||||||
|
disabled={wait}
|
||||||
|
>
|
||||||
|
{wait ? <Spinner /> : <></>}
|
||||||
|
Aggiungi
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</form>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddClientDialog;
|
||||||
121
src/components/add-intervento.tsx
Normal file
121
src/components/add-intervento.tsx
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
|
import { Plus } from "lucide-react";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import DatePicker from "./date-picker";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
|
||||||
|
const AddInterventoDialog = ({ id }: { id: string }) => {
|
||||||
|
//const [nome, setNome] = useState("");
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
const [openDatePicker, setOpenDatePicker] = useState(false);
|
||||||
|
const [data, setData] = useState<Date | undefined>(new Date());
|
||||||
|
const [lavoro, setLavoro] = useState<string>("");
|
||||||
|
const [fattura, setFattura] = useState<Boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dialog open={openDialog} onOpenChange={setOpenDialog}>
|
||||||
|
<form className="z-10">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline" type="button">
|
||||||
|
<Plus className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Aggiungi intervento</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Aggiungi intervento</DialogTitle>
|
||||||
|
{/*<DialogDescription>
|
||||||
|
Make changes to your profile here. Click save when you're
|
||||||
|
done.
|
||||||
|
</DialogDescription>*/}
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="date">Data</Label>
|
||||||
|
<DatePicker
|
||||||
|
open={openDatePicker}
|
||||||
|
setOpen={setOpenDatePicker}
|
||||||
|
date={data}
|
||||||
|
setDate={setData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="lavoro">Lavoro</Label>
|
||||||
|
<Input
|
||||||
|
id="lavoro"
|
||||||
|
name="lavoro"
|
||||||
|
placeholder="Lavoro eseguito..."
|
||||||
|
onChange={(e) => setLavoro(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="gap-3 flex items-center mt-2">
|
||||||
|
<Checkbox
|
||||||
|
id="fattura"
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
checked === "indeterminate"
|
||||||
|
? setFattura(false)
|
||||||
|
: checked
|
||||||
|
? setFattura(true)
|
||||||
|
: setFattura(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="fattura">Fattura</Label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button variant="outline">Cancella</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
const res = await fetch("/api/interventi", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: id,
|
||||||
|
data: data,
|
||||||
|
fattura: fattura,
|
||||||
|
lavoro: lavoro,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (res.status == 200) {
|
||||||
|
setOpenDialog(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Aggiungi
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</form>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddInterventoDialog;
|
||||||
137
src/components/add-registratore.tsx
Normal file
137
src/components/add-registratore.tsx
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
|
import { Plus } from "lucide-react";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useState } from "react";
|
||||||
|
import DatePicker from "./date-picker";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import ModelPicker from "./model-picker";
|
||||||
|
|
||||||
|
const AddRegistratoreDialog = ({ id }: { id: Number }) => {
|
||||||
|
//const [nome, setNome] = useState("");
|
||||||
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
const [openData, setOpenData] = useState(false);
|
||||||
|
const [openModello, setOpenModello] = useState(false);
|
||||||
|
const [data, setData] = useState<Date | undefined>();
|
||||||
|
const [seriale, setSeriale] = useState<string>("");
|
||||||
|
const [modello, setModello] = useState<Date | undefined>();
|
||||||
|
const [fattura, setFattura] = useState<Boolean>();
|
||||||
|
|
||||||
|
const modelli = [
|
||||||
|
{
|
||||||
|
value: "FORM100",
|
||||||
|
label: "Form 100",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "FORM200",
|
||||||
|
label: "Form 200",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "FORM200PLUS",
|
||||||
|
label: "Form 200 Plus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "FORM500",
|
||||||
|
label: "Form 500",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dialog open={openDialog} onOpenChange={setOpenDialog}>
|
||||||
|
<form className="z-10">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline" type="button">
|
||||||
|
<Plus className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Aggiungi registratore</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Aggiungi registratore</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="seriale">Seriale</Label>
|
||||||
|
<Input
|
||||||
|
id="seriale"
|
||||||
|
name="seriale"
|
||||||
|
placeholder="Seriale"
|
||||||
|
onChange={(e) => setSeriale(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="date">Data di acquisto</Label>
|
||||||
|
<DatePicker
|
||||||
|
open={openData}
|
||||||
|
setOpen={setOpenData}
|
||||||
|
date={data}
|
||||||
|
setDate={setData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<Label htmlFor="lavoro">Modello</Label>
|
||||||
|
<ModelPicker
|
||||||
|
open={openModello}
|
||||||
|
setOpen={setOpenModello}
|
||||||
|
value={modello}
|
||||||
|
setValue={setModello}
|
||||||
|
modelli={modelli}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button variant="outline">Cancella</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
const res = await fetch("/api/registratori", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: id,
|
||||||
|
seriale: seriale,
|
||||||
|
data: data,
|
||||||
|
modello: modello,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status == 200) {
|
||||||
|
setOpenDialog(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Aggiungi
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</form>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddRegistratoreDialog;
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Home, Map } from "lucide-react";
|
import { Home, Map } from "lucide-react";
|
||||||
|
|
||||||
import { NavUser } from "@/components/nav-user";
|
import { NavUser } from "@/components/nav-user";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Avatar, AvatarImage } from "./ui/avatar";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
SidebarContent,
|
SidebarContent,
|
||||||
|
|
@ -18,11 +19,15 @@ import {
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
useSidebar,
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import {
|
||||||
import { Avatar, AvatarImage } from "./ui/avatar";
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
import { useState, useEffect } from "react";
|
TooltipTrigger,
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
} from "@/components/ui/tooltip";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { XIcon } from "lucide-react";
|
||||||
|
import AddClientDialog from "./add-client";
|
||||||
|
import { Cliente } from "@/generated/prisma";
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
user: {
|
user: {
|
||||||
|
|
@ -33,17 +38,28 @@ const data = {
|
||||||
navMain: [
|
navMain: [
|
||||||
{
|
{
|
||||||
title: "Dashboard",
|
title: "Dashboard",
|
||||||
|
page: "dashboard",
|
||||||
url: "/dashboard",
|
url: "/dashboard",
|
||||||
icon: Home,
|
icon: Home,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
|
isVisible: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Map",
|
title: "Map",
|
||||||
|
page: "map",
|
||||||
url: "/map",
|
url: "/map",
|
||||||
icon: Map,
|
icon: Map,
|
||||||
isActive: false,
|
isActive: false,
|
||||||
|
isVisible: true,
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
|
title: "Dettaglio cliente",
|
||||||
|
page: "client",
|
||||||
|
url: "/client",
|
||||||
|
icon: Home,
|
||||||
|
isVisible: false,
|
||||||
|
},
|
||||||
|
] /*
|
||||||
clienti: [
|
clienti: [
|
||||||
{
|
{
|
||||||
name: "Savoldi Ettore",
|
name: "Savoldi Ettore",
|
||||||
|
|
@ -115,16 +131,26 @@ const data = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],*/,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [clientPathname, setClientPathname] = useState("");
|
const [clientPathname, setClientPathname] = useState("");
|
||||||
const [clienti, setClienti] = React.useState(data.clienti);
|
const [clienti, setClienti] = useState<Array<Cliente>>();
|
||||||
const { setOpen } = useSidebar();
|
const { setOpen } = useSidebar();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const data = await fetch("/api/clienti");
|
||||||
|
const x = await data.json();
|
||||||
|
console.log(x.clients);
|
||||||
|
setClienti(x.clients);
|
||||||
|
console.log(clienti);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setClientPathname(pathname);
|
setClientPathname(pathname);
|
||||||
}, [pathname]);
|
}, [pathname]);
|
||||||
|
|
@ -136,9 +162,6 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
variant="inset"
|
variant="inset"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{/* This is the first sidebar */}
|
|
||||||
{/* We disable collapsible and adjust width to icon. */}
|
|
||||||
{/* This will make the sidebar appear as icons. */}
|
|
||||||
<Sidebar
|
<Sidebar
|
||||||
collapsible="none"
|
collapsible="none"
|
||||||
className="w-[calc(var(--sidebar-width-icon)+1px)]! border-r"
|
className="w-[calc(var(--sidebar-width-icon)+1px)]! border-r"
|
||||||
|
|
@ -175,7 +198,11 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
<SidebarGroupContent className="px-1.5 md:px-0">
|
<SidebarGroupContent className="px-1.5 md:px-0">
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
{data.navMain.map((item) => (
|
{data.navMain.map((item) => {
|
||||||
|
if (!item.isVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return (
|
||||||
<SidebarMenuItem key={item.title}>
|
<SidebarMenuItem key={item.title}>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
tooltip={{
|
tooltip={{
|
||||||
|
|
@ -183,15 +210,6 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
hidden: false,
|
hidden: false,
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const clienti = data.clienti.sort(
|
|
||||||
() => Math.random() - 0.5,
|
|
||||||
);
|
|
||||||
setClienti(
|
|
||||||
clienti.slice(
|
|
||||||
0,
|
|
||||||
Math.max(5, Math.floor(Math.random() * 10) + 1),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
if (clientPathname != item.url) {
|
if (clientPathname != item.url) {
|
||||||
router.push(item.url);
|
router.push(item.url);
|
||||||
|
|
@ -204,7 +222,8 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
<span>{item.title}</span>
|
<span>{item.title}</span>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarGroupContent>
|
</SidebarGroupContent>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
|
|
@ -214,8 +233,6 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
</SidebarFooter>
|
</SidebarFooter>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
|
|
||||||
{/* This is the second sidebar */}
|
|
||||||
{/* We disable collapsible and let it fill remaining space */}
|
|
||||||
<Sidebar collapsible="none" className="hidden flex-1 md:flex">
|
<Sidebar collapsible="none" className="hidden flex-1 md:flex">
|
||||||
<SidebarHeader className="gap-3.5 border-b p-4">
|
<SidebarHeader className="gap-3.5 border-b p-4">
|
||||||
<div className="flex w-full items-center justify-between">
|
<div className="flex w-full items-center justify-between">
|
||||||
|
|
@ -225,31 +242,56 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||||
: data.navMain.find((entry) => entry.url == clientPathname)
|
: data.navMain.find((entry) => entry.url == clientPathname)
|
||||||
?.title}
|
?.title}
|
||||||
</div>
|
</div>
|
||||||
{/*<Label className="flex items-center gap-2 text-sm">
|
|
||||||
<span>Unreads</span>
|
|
||||||
<Switch className="shadow-none" />
|
|
||||||
</Label>*/}
|
|
||||||
</div>
|
</div>
|
||||||
<SidebarInput placeholder="Type to search..." />
|
<div className="flex flex-row max-h-max gap-2">
|
||||||
|
<SidebarInput
|
||||||
|
placeholder="Digita per cercare..."
|
||||||
|
className="h-full"
|
||||||
|
/>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
if (clientPathname.split("/")[1] == "map") {
|
||||||
|
router.replace("/map", undefined);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<XIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Resetta ricerca</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<AddClientDialog />
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<SidebarGroup className="px-0">
|
<SidebarGroup className="px-0">
|
||||||
<SidebarGroupContent className="">
|
<SidebarGroupContent className="">
|
||||||
{clienti.map((cliente) => (
|
{clienti?.map((cliente) => (
|
||||||
<a
|
<span
|
||||||
href="#"
|
onClick={() => {
|
||||||
onClick={() => router.push(`/client/${cliente.name}`)}
|
let path = clientPathname.split("/")[1];
|
||||||
key={cliente.name}
|
if (path == "dashboard" || path == "") {
|
||||||
className="w-11/12 mx-auto rounded-md hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex flex-col items-start gap-2 border-b p-4 text-sm leading-tight whitespace-nowrap last:border-b-0"
|
path = "client";
|
||||||
|
}
|
||||||
|
router.push(`/${path}?client=${cliente.id}`);
|
||||||
|
}}
|
||||||
|
key={cliente.id}
|
||||||
|
className="hover:cursor-pointer w-11/12 mx-auto rounded-md hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex flex-col items-start gap-2 border-b p-4 text-sm leading-tight whitespace-nowrap last:border-b-0"
|
||||||
>
|
>
|
||||||
<span className="font-medium">{cliente.name}</span>
|
<span className="font-medium">{cliente.ragione_sociale}</span>
|
||||||
<div className="flex w-full items-center gap-2">
|
<div className="flex w-full items-center gap-2">
|
||||||
<span className="text-xs">{cliente.sede}</span>
|
<span className="text-xs">
|
||||||
</div>
|
{cliente.sede.length > 35
|
||||||
<span className="line-clamp-2 w-[260px] text-xs whitespace-break-spaces">
|
? cliente.sede.substring(0, 35) + "..."
|
||||||
{cliente.registratori[0].prossima_verifica}
|
: cliente.sede}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
|
||||||
))}
|
))}
|
||||||
</SidebarGroupContent>
|
</SidebarGroupContent>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,15 @@ import {
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Edit, Plus } from "lucide-react";
|
import { Edit, Plus } from "lucide-react";
|
||||||
|
import { Cliente } from "@/generated/prisma";
|
||||||
|
import AddRegistratoreDialog from "./add-registratore";
|
||||||
|
|
||||||
const ClientCard = ({client}: {client: any}) => {
|
const ClientCard = ({ cliente }: { cliente: Cliente }) => {
|
||||||
return (
|
return (
|
||||||
<Card className="@container/card">
|
<Card className="@container/card">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-xl font-semibold tabular-nums @[250px]/card:text-2xl">
|
<CardTitle className="text-xl font-semibold tabular-nums @[250px]/card:text-2xl">
|
||||||
{client}
|
{cliente.ragione_sociale}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardAction className="flex items-center gap-4">
|
<CardAction className="flex items-center gap-4">
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
|
@ -31,37 +33,20 @@ const ClientCard = ({client}: {client: any}) => {
|
||||||
<p>Modifica dettagli</p>
|
<p>Modifica dettagli</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip>
|
<AddRegistratoreDialog id={cliente.id} />
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button variant="outline">
|
|
||||||
<Plus className="size-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Aggiungi registratore</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</CardAction>
|
</CardAction>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex items-start gap-20 text-md">
|
<CardContent className="flex items-start gap-20 text-md">
|
||||||
<div className="flex-col items-start gap-1.5 text-md">
|
|
||||||
<div className="text-muted-foreground">Ragione Sociale</div>
|
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
|
||||||
Acconciature Uomo
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex-col items-start gap-1.5 text-md">
|
<div className="flex-col items-start gap-1.5 text-md">
|
||||||
<div className="text-muted-foreground">Partita IVA</div>
|
<div className="text-muted-foreground">Partita IVA</div>
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
<div className="line-clamp-1 flex gap-2 font-medium">
|
||||||
13407520172
|
{cliente.partita_iva}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-col items-start gap-1.5 text-md">
|
<div className="flex-col items-start gap-1.5 text-md">
|
||||||
<div className="text-muted-foreground">
|
<div className="text-muted-foreground">Numero di Telefono</div>
|
||||||
Numero di Telefono
|
|
||||||
</div>
|
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
<div className="line-clamp-1 flex gap-2 font-medium">
|
||||||
0301547854
|
{cliente.telefono}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-col items-start gap-1.5 text-md">
|
<div className="flex-col items-start gap-1.5 text-md">
|
||||||
|
|
@ -70,9 +55,9 @@ const ClientCard = ({client}: {client: any}) => {
|
||||||
<a
|
<a
|
||||||
className="text-blue-700 italic underline"
|
className="text-blue-700 italic underline"
|
||||||
target="blank"
|
target="blank"
|
||||||
href="https://maps.app.goo.gl/9uNbw2a62ZCCjkQc7"
|
href={cliente.sede_url}
|
||||||
>
|
>
|
||||||
Via Umberto I 60/T, Flero (BS)
|
{cliente.sede}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -82,7 +67,7 @@ const ClientCard = ({client}: {client: any}) => {
|
||||||
<a
|
<a
|
||||||
className="text-blue-700 italic underline"
|
className="text-blue-700 italic underline"
|
||||||
target="blank"
|
target="blank"
|
||||||
href="https://google.com"
|
href={cliente.contratto}
|
||||||
>
|
>
|
||||||
LINK
|
LINK
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -90,7 +75,7 @@ const ClientCard = ({client}: {client: any}) => {
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default ClientCard;
|
export default ClientCard;
|
||||||
49
src/components/date-picker.tsx
Normal file
49
src/components/date-picker.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { ChevronDownIcon } from "lucide-react";
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
|
const DatePicker = ({
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
date,
|
||||||
|
setDate,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
setOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
|
date: Date | undefined;
|
||||||
|
setDate: Dispatch<SetStateAction<Date | undefined>>;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
id="date"
|
||||||
|
className="w-full justify-between font-normal"
|
||||||
|
>
|
||||||
|
{date ? date.toLocaleDateString() : "Select date"}
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-auto overflow-hidden p-0" align="start">
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={date}
|
||||||
|
captionLayout="dropdown"
|
||||||
|
onSelect={(date) => {
|
||||||
|
setDate(date);
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DatePicker;
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardAction,
|
CardAction,
|
||||||
|
|
@ -24,43 +26,15 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Edit, Plus } from "lucide-react";
|
import { Edit, Plus } from "lucide-react";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useParams } from "next/navigation";
|
import { Cliente, Registratore } from "@/generated/prisma";
|
||||||
|
import AddInterventoDialog from "./add-intervento";
|
||||||
type Invervento = {
|
|
||||||
id: string,
|
|
||||||
data: string,
|
|
||||||
lavoro: string,
|
|
||||||
fattura: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type Registratore = {
|
|
||||||
seriale: string,
|
|
||||||
acquisto: string,
|
|
||||||
ultima_verifica: string,
|
|
||||||
prossima_verifica: string,
|
|
||||||
interventi: Array<Invervento>
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cliente = {
|
|
||||||
name: string,
|
|
||||||
email: string,
|
|
||||||
ragione_sociale: string,
|
|
||||||
p_iva: string,
|
|
||||||
telefono: string,
|
|
||||||
sede: string,
|
|
||||||
sede_url: string,
|
|
||||||
contratto: string,
|
|
||||||
registratori: Array<Registratore>
|
|
||||||
}
|
|
||||||
|
|
||||||
const DeviceCard = ({clienti}: {clienti:Array<Cliente>}) => {
|
|
||||||
const { client } = useParams();
|
|
||||||
|
|
||||||
|
const DeviceCard = ({ registratore }: { registratore: Registratore }) => {
|
||||||
return (
|
return (
|
||||||
<Card className="@container/card">
|
<Card className="@container/card">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-xl font-semibold tabular-nums @[250px]/card:text-2xl">
|
<CardTitle className="text-xl font-semibold tabular-nums @[250px]/card:text-2xl">
|
||||||
FORM 100
|
{registratore.modello}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardAction className="flex items-center gap-4">
|
<CardAction className="flex items-center gap-4">
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
|
@ -73,16 +47,7 @@ const DeviceCard = ({clienti}: {clienti:Array<Cliente>}) => {
|
||||||
<p>Modifica dettagli</p>
|
<p>Modifica dettagli</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip>
|
<AddInterventoDialog id={registratore.id} />
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button variant="outline">
|
|
||||||
<Plus className="size-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Aggiungi intervento</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</CardAction>
|
</CardAction>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-col gap-5 text-md">
|
<CardContent className="flex flex-col gap-5 text-md">
|
||||||
|
|
@ -90,27 +55,37 @@ const DeviceCard = ({clienti}: {clienti:Array<Cliente>}) => {
|
||||||
<div className="flex-col items-start gap-1.5 text-md">
|
<div className="flex-col items-start gap-1.5 text-md">
|
||||||
<div className="text-muted-foreground">Seriale</div>
|
<div className="text-muted-foreground">Seriale</div>
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
<div className="line-clamp-1 flex gap-2 font-medium">
|
||||||
80E100548745
|
{registratore.id}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-col items-start gap-1.5 text-md">
|
<div className="flex-col items-start gap-1.5 text-md">
|
||||||
<div className="text-muted-foreground">Data acquisto</div>
|
<div className="text-muted-foreground">Data acquisto</div>
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
<div className="line-clamp-1 flex gap-2 font-medium">
|
||||||
15/10/2019
|
{registratore.data_acquisto
|
||||||
|
? new Date(registratore.data_acquisto).toLocaleDateString(
|
||||||
|
"it-IT",
|
||||||
|
)
|
||||||
|
: "--"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-col items-start gap-1.5 text-md">
|
<div className="flex-col items-start gap-1.5 text-md">
|
||||||
<div className="text-muted-foreground">Ultima verifica</div>
|
<div className="text-muted-foreground">Ultima verifica</div>
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
<div className="line-clamp-1 flex gap-2 font-medium">
|
||||||
15/10/2025
|
{registratore.ultima_verifica
|
||||||
|
? new Date(registratore.ultima_verifica).toLocaleDateString(
|
||||||
|
"it-IT",
|
||||||
|
)
|
||||||
|
: "--"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-col items-start gap-1.5 text-md">
|
<div className="flex-col items-start gap-1.5 text-md">
|
||||||
<div className="text-muted-foreground">
|
<div className="text-muted-foreground">Prossima verifica</div>
|
||||||
Prossima verifica
|
|
||||||
</div>
|
|
||||||
<div className="line-clamp-1 flex gap-2 font-medium">
|
<div className="line-clamp-1 flex gap-2 font-medium">
|
||||||
15/10/2026
|
{registratore.prossima_verifica
|
||||||
|
? new Date(registratore.prossima_verifica).toLocaleDateString(
|
||||||
|
"it-IT",
|
||||||
|
)
|
||||||
|
: "--"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -127,15 +102,10 @@ const DeviceCard = ({clienti}: {clienti:Array<Cliente>}) => {
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{clienti
|
{registratore.interventi.map((intervento) => (
|
||||||
.find(
|
|
||||||
(cliente) =>
|
|
||||||
cliente.name == decodeURIComponent(client?.toString() || ""),
|
|
||||||
)
|
|
||||||
?.registratori[0].interventi.map((intervento) => (
|
|
||||||
<TableRow key={intervento.id}>
|
<TableRow key={intervento.id}>
|
||||||
<TableCell className="font-medium">
|
<TableCell className="font-medium">
|
||||||
{intervento.data}
|
{new Date(intervento.data).toLocaleDateString("it-IT")}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{intervento.lavoro}</TableCell>
|
<TableCell>{intervento.lavoro}</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
|
|
@ -162,7 +132,7 @@ const DeviceCard = ({clienti}: {clienti:Array<Cliente>}) => {
|
||||||
</Button>
|
</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default DeviceCard;
|
export default DeviceCard;
|
||||||
|
|
@ -1,24 +1,47 @@
|
||||||
import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet";
|
import { MapContainer, Marker, Popup, TileLayer } from "react-leaflet";
|
||||||
import { Icon } from "leaflet";
|
import { Icon } from "leaflet";
|
||||||
import "leaflet/dist/leaflet.css";
|
import "leaflet/dist/leaflet.css";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Map as MapIcon } from "lucide-react";
|
||||||
|
import { Cliente } from "@/generated/prisma";
|
||||||
|
|
||||||
const Map = () => {
|
const Map = ({
|
||||||
|
highlight,
|
||||||
|
clienti,
|
||||||
|
}: {
|
||||||
|
highlight?: Cliente;
|
||||||
|
clienti?: Array<Cliente>;
|
||||||
|
}) => {
|
||||||
const positions = [
|
const positions = [
|
||||||
{
|
{
|
||||||
name: "Autogeneral",
|
name: "Autogeneral",
|
||||||
position: [45.49285083101236, 10.15951437254693],
|
position: [45.49285083101236, 10.15951437254693],
|
||||||
|
sede_url: "https://maps.app.goo.gl/9uNbw2a62ZCCjkQc7",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Delizie e Sapori (Cuor di gelato)",
|
name: "Delizie e Sapori (Cuor di gelato)",
|
||||||
position: [45.55425938252774, 10.227818585851844],
|
position: [45.55425938252774, 10.227818585851844],
|
||||||
|
sede_url: "https://maps.app.goo.gl/9uNbw2a62ZCCjkQc7",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Casa dell'Ottica di Zanotti Giulio",
|
name: "Casa dell'Ottica di Zanotti Giulio",
|
||||||
position: [45.53644055688526, 10.222584658771389],
|
position: [45.53644055688526, 10.222584658771389],
|
||||||
|
sede_url: "https://maps.app.goo.gl/9uNbw2a62ZCCjkQc7",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Nuova Ottica",
|
name: "Nuova Ottica",
|
||||||
position: [45.48257540298808, 10.23957216093029],
|
position: [45.48257540298808, 10.23957216093029],
|
||||||
|
sede_url: "https://maps.app.goo.gl/9uNbw2a62ZCCjkQc7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Savoldi Ettore",
|
||||||
|
position: [45.486316837251415, 10.1737522099259],
|
||||||
|
sede_url: "https://maps.app.goo.gl/9uNbw2a62ZCCjkQc7",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -30,31 +53,95 @@ const Map = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MapContainer
|
<MapContainer
|
||||||
center={positions[0].position}
|
center={
|
||||||
zoom={10}
|
highlight
|
||||||
|
? [highlight.lat, highlight.lon]
|
||||||
|
: [45.54157745559809, 10.211896906975962]
|
||||||
|
}
|
||||||
|
zoom={13}
|
||||||
scrollWheelZoom={true}
|
scrollWheelZoom={true}
|
||||||
className="m-0 p-0 h-[99%] w-[99%] mx-[0.5%] my-[0.5%] rounded-md"
|
className="m-0 p-0 h-[99%] w-[99%] mx-[0.5%] my-[0.5%] rounded-md z-0"
|
||||||
>
|
>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
url={`https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`}
|
url={`https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`}
|
||||||
/>
|
/>
|
||||||
{positions.map((client) => (
|
{highlight == null ? (
|
||||||
|
clienti ? (
|
||||||
|
clienti.map((client) => (
|
||||||
<Marker
|
<Marker
|
||||||
position={client.position}
|
position={[client.lat, client.lon]}
|
||||||
|
draggable={false}
|
||||||
|
animate={false}
|
||||||
|
icon={customMarker}
|
||||||
|
key={client.id}
|
||||||
|
>
|
||||||
|
<Popup className="">
|
||||||
|
<div className="flex flex-row gap-5">
|
||||||
|
<div>
|
||||||
|
<span className="block text-sm font-bold">
|
||||||
|
{client.ragione_sociale}
|
||||||
|
</span>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<span className="text-xs font-semibold">P.IVA</span>
|
||||||
|
<span className="text-xs">{client.partita_iva}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<a href={client.sede_url} target="blank">
|
||||||
|
<Button variant="outline">
|
||||||
|
<MapIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Resetta ricerca</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</Popup>
|
||||||
|
</Marker>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Marker
|
||||||
|
position={[highlight.lat, highlight.lon]}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
animate={false}
|
animate={false}
|
||||||
icon={customMarker}
|
icon={customMarker}
|
||||||
>
|
>
|
||||||
<Popup>
|
<Popup className="">
|
||||||
<span className="block text-sm font-bold">{client.name}</span>
|
<div className="flex flex-row gap-5">
|
||||||
|
<div>
|
||||||
|
<span className="block text-sm font-bold">
|
||||||
|
{highlight.ragione_sociale}
|
||||||
|
</span>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<span className="text-xs font-semibold">P.IVA</span>
|
<span className="text-xs font-semibold">P.IVA</span>
|
||||||
<span className="text-xs">03417520172</span>
|
<span className="text-xs">03417520172</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<a href={highlight.sede_url} target="blank">
|
||||||
|
<Button variant="outline">
|
||||||
|
<MapIcon className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Resetta ricerca</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</Popup>
|
</Popup>
|
||||||
</Marker>
|
</Marker>
|
||||||
))}
|
)}
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
79
src/components/model-picker.tsx
Normal file
79
src/components/model-picker.tsx
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { Check, ChevronsUpDown } from "lucide-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/components/ui/command";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
|
const ModelPicker = ({
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
value,
|
||||||
|
setValue,
|
||||||
|
modelli,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
setOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
|
value: any;
|
||||||
|
setValue: Dispatch<SetStateAction<any>>;
|
||||||
|
modelli: Array<any>;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
aria-expanded={open}
|
||||||
|
className="w-full justify-between"
|
||||||
|
>
|
||||||
|
{value
|
||||||
|
? modelli.find((modello) => modello.value === value)?.label
|
||||||
|
: "Select framework..."}
|
||||||
|
<ChevronsUpDown className="opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-full p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search framework..." className="h-9" />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No framework found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{modelli.map((modello) => (
|
||||||
|
<CommandItem
|
||||||
|
key={modello.value}
|
||||||
|
value={modello.value}
|
||||||
|
onSelect={(currentValue) => {
|
||||||
|
setValue(currentValue === value ? "" : currentValue);
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{modello.label}
|
||||||
|
<Check
|
||||||
|
className={cn(
|
||||||
|
"ml-auto",
|
||||||
|
value === modello.value ? "opacity-100" : "opacity-0",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModelPicker;
|
||||||
216
src/components/ui/calendar.tsx
Normal file
216
src/components/ui/calendar.tsx
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import {
|
||||||
|
ChevronDownIcon,
|
||||||
|
ChevronLeftIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
} from "lucide-react"
|
||||||
|
import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button, buttonVariants } from "@/components/ui/button"
|
||||||
|
|
||||||
|
function Calendar({
|
||||||
|
className,
|
||||||
|
classNames,
|
||||||
|
showOutsideDays = true,
|
||||||
|
captionLayout = "label",
|
||||||
|
buttonVariant = "ghost",
|
||||||
|
formatters,
|
||||||
|
components,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DayPicker> & {
|
||||||
|
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
|
||||||
|
}) {
|
||||||
|
const defaultClassNames = getDefaultClassNames()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DayPicker
|
||||||
|
showOutsideDays={showOutsideDays}
|
||||||
|
className={cn(
|
||||||
|
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
||||||
|
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
||||||
|
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
captionLayout={captionLayout}
|
||||||
|
formatters={{
|
||||||
|
formatMonthDropdown: (date) =>
|
||||||
|
date.toLocaleString("default", { month: "short" }),
|
||||||
|
...formatters,
|
||||||
|
}}
|
||||||
|
classNames={{
|
||||||
|
root: cn("w-fit", defaultClassNames.root),
|
||||||
|
months: cn(
|
||||||
|
"flex gap-4 flex-col md:flex-row relative",
|
||||||
|
defaultClassNames.months
|
||||||
|
),
|
||||||
|
month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
|
||||||
|
nav: cn(
|
||||||
|
"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
|
||||||
|
defaultClassNames.nav
|
||||||
|
),
|
||||||
|
button_previous: cn(
|
||||||
|
buttonVariants({ variant: buttonVariant }),
|
||||||
|
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
|
||||||
|
defaultClassNames.button_previous
|
||||||
|
),
|
||||||
|
button_next: cn(
|
||||||
|
buttonVariants({ variant: buttonVariant }),
|
||||||
|
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
|
||||||
|
defaultClassNames.button_next
|
||||||
|
),
|
||||||
|
month_caption: cn(
|
||||||
|
"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
|
||||||
|
defaultClassNames.month_caption
|
||||||
|
),
|
||||||
|
dropdowns: cn(
|
||||||
|
"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
|
||||||
|
defaultClassNames.dropdowns
|
||||||
|
),
|
||||||
|
dropdown_root: cn(
|
||||||
|
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
|
||||||
|
defaultClassNames.dropdown_root
|
||||||
|
),
|
||||||
|
dropdown: cn(
|
||||||
|
"absolute bg-popover inset-0 opacity-0",
|
||||||
|
defaultClassNames.dropdown
|
||||||
|
),
|
||||||
|
caption_label: cn(
|
||||||
|
"select-none font-medium",
|
||||||
|
captionLayout === "label"
|
||||||
|
? "text-sm"
|
||||||
|
: "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
|
||||||
|
defaultClassNames.caption_label
|
||||||
|
),
|
||||||
|
table: "w-full border-collapse",
|
||||||
|
weekdays: cn("flex", defaultClassNames.weekdays),
|
||||||
|
weekday: cn(
|
||||||
|
"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
|
||||||
|
defaultClassNames.weekday
|
||||||
|
),
|
||||||
|
week: cn("flex w-full mt-2", defaultClassNames.week),
|
||||||
|
week_number_header: cn(
|
||||||
|
"select-none w-(--cell-size)",
|
||||||
|
defaultClassNames.week_number_header
|
||||||
|
),
|
||||||
|
week_number: cn(
|
||||||
|
"text-[0.8rem] select-none text-muted-foreground",
|
||||||
|
defaultClassNames.week_number
|
||||||
|
),
|
||||||
|
day: cn(
|
||||||
|
"relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
|
||||||
|
props.showWeekNumber
|
||||||
|
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
|
||||||
|
: "[&:first-child[data-selected=true]_button]:rounded-l-md",
|
||||||
|
defaultClassNames.day
|
||||||
|
),
|
||||||
|
range_start: cn(
|
||||||
|
"rounded-l-md bg-accent",
|
||||||
|
defaultClassNames.range_start
|
||||||
|
),
|
||||||
|
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
||||||
|
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
|
||||||
|
today: cn(
|
||||||
|
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
|
||||||
|
defaultClassNames.today
|
||||||
|
),
|
||||||
|
outside: cn(
|
||||||
|
"text-muted-foreground aria-selected:text-muted-foreground",
|
||||||
|
defaultClassNames.outside
|
||||||
|
),
|
||||||
|
disabled: cn(
|
||||||
|
"text-muted-foreground opacity-50",
|
||||||
|
defaultClassNames.disabled
|
||||||
|
),
|
||||||
|
hidden: cn("invisible", defaultClassNames.hidden),
|
||||||
|
...classNames,
|
||||||
|
}}
|
||||||
|
components={{
|
||||||
|
Root: ({ className, rootRef, ...props }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="calendar"
|
||||||
|
ref={rootRef}
|
||||||
|
className={cn(className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Chevron: ({ className, orientation, ...props }) => {
|
||||||
|
if (orientation === "left") {
|
||||||
|
return (
|
||||||
|
<ChevronLeftIcon className={cn("size-4", className)} {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orientation === "right") {
|
||||||
|
return (
|
||||||
|
<ChevronRightIcon
|
||||||
|
className={cn("size-4", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChevronDownIcon className={cn("size-4", className)} {...props} />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
DayButton: CalendarDayButton,
|
||||||
|
WeekNumber: ({ children, ...props }) => {
|
||||||
|
return (
|
||||||
|
<td {...props}>
|
||||||
|
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
...components,
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CalendarDayButton({
|
||||||
|
className,
|
||||||
|
day,
|
||||||
|
modifiers,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DayButton>) {
|
||||||
|
const defaultClassNames = getDefaultClassNames()
|
||||||
|
|
||||||
|
const ref = React.useRef<HTMLButtonElement>(null)
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (modifiers.focused) ref.current?.focus()
|
||||||
|
}, [modifiers.focused])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
ref={ref}
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
data-day={day.date.toLocaleDateString()}
|
||||||
|
data-selected-single={
|
||||||
|
modifiers.selected &&
|
||||||
|
!modifiers.range_start &&
|
||||||
|
!modifiers.range_end &&
|
||||||
|
!modifiers.range_middle
|
||||||
|
}
|
||||||
|
data-range-start={modifiers.range_start}
|
||||||
|
data-range-end={modifiers.range_end}
|
||||||
|
data-range-middle={modifiers.range_middle}
|
||||||
|
className={cn(
|
||||||
|
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
||||||
|
defaultClassNames.day,
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Calendar, CalendarDayButton }
|
||||||
184
src/components/ui/command.tsx
Normal file
184
src/components/ui/command.tsx
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { Command as CommandPrimitive } from "cmdk"
|
||||||
|
import { SearchIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog"
|
||||||
|
|
||||||
|
function Command({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof CommandPrimitive>) {
|
||||||
|
return (
|
||||||
|
<CommandPrimitive
|
||||||
|
data-slot="command"
|
||||||
|
className={cn(
|
||||||
|
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommandDialog({
|
||||||
|
title = "Command Palette",
|
||||||
|
description = "Search for a command to run...",
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
showCloseButton = true,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof Dialog> & {
|
||||||
|
title?: string
|
||||||
|
description?: string
|
||||||
|
className?: string
|
||||||
|
showCloseButton?: boolean
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dialog {...props}>
|
||||||
|
<DialogHeader className="sr-only">
|
||||||
|
<DialogTitle>{title}</DialogTitle>
|
||||||
|
<DialogDescription>{description}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogContent
|
||||||
|
className={cn("overflow-hidden p-0", className)}
|
||||||
|
showCloseButton={showCloseButton}
|
||||||
|
>
|
||||||
|
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||||
|
{children}
|
||||||
|
</Command>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommandInput({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof CommandPrimitive.Input>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="command-input-wrapper"
|
||||||
|
className="flex h-9 items-center gap-2 border-b px-3"
|
||||||
|
>
|
||||||
|
<SearchIcon className="size-4 shrink-0 opacity-50" />
|
||||||
|
<CommandPrimitive.Input
|
||||||
|
data-slot="command-input"
|
||||||
|
className={cn(
|
||||||
|
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommandList({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof CommandPrimitive.List>) {
|
||||||
|
return (
|
||||||
|
<CommandPrimitive.List
|
||||||
|
data-slot="command-list"
|
||||||
|
className={cn(
|
||||||
|
"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommandEmpty({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof CommandPrimitive.Empty>) {
|
||||||
|
return (
|
||||||
|
<CommandPrimitive.Empty
|
||||||
|
data-slot="command-empty"
|
||||||
|
className="py-6 text-center text-sm"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommandGroup({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof CommandPrimitive.Group>) {
|
||||||
|
return (
|
||||||
|
<CommandPrimitive.Group
|
||||||
|
data-slot="command-group"
|
||||||
|
className={cn(
|
||||||
|
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommandSeparator({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof CommandPrimitive.Separator>) {
|
||||||
|
return (
|
||||||
|
<CommandPrimitive.Separator
|
||||||
|
data-slot="command-separator"
|
||||||
|
className={cn("bg-border -mx-1 h-px", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommandItem({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof CommandPrimitive.Item>) {
|
||||||
|
return (
|
||||||
|
<CommandPrimitive.Item
|
||||||
|
data-slot="command-item"
|
||||||
|
className={cn(
|
||||||
|
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CommandShortcut({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"span">) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
data-slot="command-shortcut"
|
||||||
|
className={cn(
|
||||||
|
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Command,
|
||||||
|
CommandDialog,
|
||||||
|
CommandInput,
|
||||||
|
CommandList,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandItem,
|
||||||
|
CommandShortcut,
|
||||||
|
CommandSeparator,
|
||||||
|
}
|
||||||
143
src/components/ui/dialog.tsx
Normal file
143
src/components/ui/dialog.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||||
|
import { XIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Dialog({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||||
|
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogTrigger({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||||
|
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogPortal({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||||
|
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogClose({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||||
|
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogOverlay({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Overlay
|
||||||
|
data-slot="dialog-overlay"
|
||||||
|
className={cn(
|
||||||
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
showCloseButton = true,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
||||||
|
showCloseButton?: boolean
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<DialogPortal data-slot="dialog-portal">
|
||||||
|
<DialogOverlay />
|
||||||
|
<DialogPrimitive.Content
|
||||||
|
data-slot="dialog-content"
|
||||||
|
className={cn(
|
||||||
|
"bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
{showCloseButton && (
|
||||||
|
<DialogPrimitive.Close
|
||||||
|
data-slot="dialog-close"
|
||||||
|
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||||
|
>
|
||||||
|
<XIcon />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
)}
|
||||||
|
</DialogPrimitive.Content>
|
||||||
|
</DialogPortal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="dialog-header"
|
||||||
|
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="dialog-footer"
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogTitle({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Title
|
||||||
|
data-slot="dialog-title"
|
||||||
|
className={cn("text-lg leading-none font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogDescription({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Description
|
||||||
|
data-slot="dialog-description"
|
||||||
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogPortal,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
}
|
||||||
48
src/components/ui/popover.tsx
Normal file
48
src/components/ui/popover.tsx
Normal 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 }
|
||||||
16
src/components/ui/spinner.tsx
Normal file
16
src/components/ui/spinner.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Loader2Icon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
|
||||||
|
return (
|
||||||
|
<Loader2Icon
|
||||||
|
role="status"
|
||||||
|
aria-label="Loading"
|
||||||
|
className={cn("size-4 animate-spin", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Spinner }
|
||||||
1
src/generated/prisma/client.d.ts
vendored
Normal file
1
src/generated/prisma/client.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./index"
|
||||||
4
src/generated/prisma/client.js
Normal file
4
src/generated/prisma/client.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||||
|
/* eslint-disable */
|
||||||
|
module.exports = { ...require('.') }
|
||||||
1
src/generated/prisma/default.d.ts
vendored
Normal file
1
src/generated/prisma/default.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./index"
|
||||||
4
src/generated/prisma/default.js
Normal file
4
src/generated/prisma/default.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||||
|
/* eslint-disable */
|
||||||
|
module.exports = { ...require('#main-entry-point') }
|
||||||
1
src/generated/prisma/edge.d.ts
vendored
Normal file
1
src/generated/prisma/edge.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./default"
|
||||||
222
src/generated/prisma/edge.js
Normal file
222
src/generated/prisma/edge.js
Normal file
File diff suppressed because one or more lines are too long
209
src/generated/prisma/index-browser.js
Normal file
209
src/generated/prisma/index-browser.js
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
|
||||||
|
const {
|
||||||
|
Decimal,
|
||||||
|
objectEnumValues,
|
||||||
|
makeStrictEnum,
|
||||||
|
Public,
|
||||||
|
getRuntime,
|
||||||
|
skip
|
||||||
|
} = require('./runtime/index-browser.js')
|
||||||
|
|
||||||
|
|
||||||
|
const Prisma = {}
|
||||||
|
|
||||||
|
exports.Prisma = Prisma
|
||||||
|
exports.$Enums = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prisma Client JS version: 6.18.0
|
||||||
|
* Query Engine version: 34b5a692b7bd79939a9a2c3ef97d816e749cda2f
|
||||||
|
*/
|
||||||
|
Prisma.prismaVersion = {
|
||||||
|
client: "6.18.0",
|
||||||
|
engine: "34b5a692b7bd79939a9a2c3ef97d816e749cda2f"
|
||||||
|
}
|
||||||
|
|
||||||
|
Prisma.PrismaClientKnownRequestError = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)};
|
||||||
|
Prisma.PrismaClientUnknownRequestError = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.PrismaClientRustPanicError = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.PrismaClientInitializationError = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.PrismaClientValidationError = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.Decimal = Decimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-export of sql-template-tag
|
||||||
|
*/
|
||||||
|
Prisma.sql = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.empty = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.join = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.raw = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.validator = Public.validator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extensions
|
||||||
|
*/
|
||||||
|
Prisma.getExtensionContext = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
Prisma.defineExtension = () => {
|
||||||
|
const runtimeName = getRuntime().prettyName;
|
||||||
|
throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||||
|
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||||
|
)}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand utilities for JSON filtering
|
||||||
|
*/
|
||||||
|
Prisma.DbNull = objectEnumValues.instances.DbNull
|
||||||
|
Prisma.JsonNull = objectEnumValues.instances.JsonNull
|
||||||
|
Prisma.AnyNull = objectEnumValues.instances.AnyNull
|
||||||
|
|
||||||
|
Prisma.NullTypes = {
|
||||||
|
DbNull: objectEnumValues.classes.DbNull,
|
||||||
|
JsonNull: objectEnumValues.classes.JsonNull,
|
||||||
|
AnyNull: objectEnumValues.classes.AnyNull
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enums
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||||
|
ReadUncommitted: 'ReadUncommitted',
|
||||||
|
ReadCommitted: 'ReadCommitted',
|
||||||
|
RepeatableRead: 'RepeatableRead',
|
||||||
|
Serializable: 'Serializable'
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.Prisma.InterventoScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
id_registratore: 'id_registratore',
|
||||||
|
data: 'data',
|
||||||
|
lavoro: 'lavoro',
|
||||||
|
fattura: 'fattura'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.RegistratoreScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
id_cliente: 'id_cliente',
|
||||||
|
modello: 'modello',
|
||||||
|
data_acquisto: 'data_acquisto',
|
||||||
|
ultima_verifica: 'ultima_verifica',
|
||||||
|
prossima_verifica: 'prossima_verifica'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.ClienteScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
ragione_sociale: 'ragione_sociale',
|
||||||
|
email: 'email',
|
||||||
|
partita_iva: 'partita_iva',
|
||||||
|
telefono: 'telefono',
|
||||||
|
sede: 'sede',
|
||||||
|
sede_url: 'sede_url',
|
||||||
|
contratto: 'contratto',
|
||||||
|
lat: 'lat',
|
||||||
|
lon: 'lon'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.SortOrder = {
|
||||||
|
asc: 'asc',
|
||||||
|
desc: 'desc'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.QueryMode = {
|
||||||
|
default: 'default',
|
||||||
|
insensitive: 'insensitive'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
};
|
||||||
|
exports.Modello = exports.$Enums.Modello = {
|
||||||
|
FORM100: 'FORM100',
|
||||||
|
FORM200: 'FORM200',
|
||||||
|
FORM200PLUS: 'FORM200PLUS',
|
||||||
|
FORM500: 'FORM500'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.ModelName = {
|
||||||
|
Intervento: 'Intervento',
|
||||||
|
Registratore: 'Registratore',
|
||||||
|
Cliente: 'Cliente'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a stub Prisma Client that will error at runtime if called.
|
||||||
|
*/
|
||||||
|
class PrismaClient {
|
||||||
|
constructor() {
|
||||||
|
return new Proxy(this, {
|
||||||
|
get(target, prop) {
|
||||||
|
let message
|
||||||
|
const runtime = getRuntime()
|
||||||
|
if (runtime.isEdge) {
|
||||||
|
message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either:
|
||||||
|
- Use Prisma Accelerate: https://pris.ly/d/accelerate
|
||||||
|
- Use Driver Adapters: https://pris.ly/d/driver-adapters
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).'
|
||||||
|
}
|
||||||
|
|
||||||
|
message += `
|
||||||
|
If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report`
|
||||||
|
|
||||||
|
throw new Error(message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.PrismaClient = PrismaClient
|
||||||
|
|
||||||
|
Object.assign(exports, Prisma)
|
||||||
6049
src/generated/prisma/index.d.ts
vendored
Normal file
6049
src/generated/prisma/index.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
243
src/generated/prisma/index.js
Normal file
243
src/generated/prisma/index.js
Normal file
File diff suppressed because one or more lines are too long
183
src/generated/prisma/package.json
Normal file
183
src/generated/prisma/package.json
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
{
|
||||||
|
"name": "prisma-client-5f7d234f39789de56f24e3f97fcb730daf5c78fb0ef54bab42a661559a87fb78",
|
||||||
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"browser": "default.js",
|
||||||
|
"exports": {
|
||||||
|
"./client": {
|
||||||
|
"require": {
|
||||||
|
"node": "./index.js",
|
||||||
|
"edge-light": "./wasm.js",
|
||||||
|
"workerd": "./wasm.js",
|
||||||
|
"worker": "./wasm.js",
|
||||||
|
"browser": "./index-browser.js",
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"node": "./index.js",
|
||||||
|
"edge-light": "./wasm.js",
|
||||||
|
"workerd": "./wasm.js",
|
||||||
|
"worker": "./wasm.js",
|
||||||
|
"browser": "./index-browser.js",
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
".": {
|
||||||
|
"require": {
|
||||||
|
"node": "./index.js",
|
||||||
|
"edge-light": "./wasm.js",
|
||||||
|
"workerd": "./wasm.js",
|
||||||
|
"worker": "./wasm.js",
|
||||||
|
"browser": "./index-browser.js",
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"node": "./index.js",
|
||||||
|
"edge-light": "./wasm.js",
|
||||||
|
"workerd": "./wasm.js",
|
||||||
|
"worker": "./wasm.js",
|
||||||
|
"browser": "./index-browser.js",
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./edge": {
|
||||||
|
"types": "./edge.d.ts",
|
||||||
|
"require": "./edge.js",
|
||||||
|
"import": "./edge.js",
|
||||||
|
"default": "./edge.js"
|
||||||
|
},
|
||||||
|
"./react-native": {
|
||||||
|
"types": "./react-native.d.ts",
|
||||||
|
"require": "./react-native.js",
|
||||||
|
"import": "./react-native.js",
|
||||||
|
"default": "./react-native.js"
|
||||||
|
},
|
||||||
|
"./extension": {
|
||||||
|
"types": "./extension.d.ts",
|
||||||
|
"require": "./extension.js",
|
||||||
|
"import": "./extension.js",
|
||||||
|
"default": "./extension.js"
|
||||||
|
},
|
||||||
|
"./index-browser": {
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"require": "./index-browser.js",
|
||||||
|
"import": "./index-browser.js",
|
||||||
|
"default": "./index-browser.js"
|
||||||
|
},
|
||||||
|
"./index": {
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"require": "./index.js",
|
||||||
|
"import": "./index.js",
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"./wasm": {
|
||||||
|
"types": "./wasm.d.ts",
|
||||||
|
"require": "./wasm.js",
|
||||||
|
"import": "./wasm.mjs",
|
||||||
|
"default": "./wasm.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/client": {
|
||||||
|
"types": "./runtime/client.d.ts",
|
||||||
|
"node": {
|
||||||
|
"require": "./runtime/client.js",
|
||||||
|
"default": "./runtime/client.js"
|
||||||
|
},
|
||||||
|
"require": "./runtime/client.js",
|
||||||
|
"import": "./runtime/client.mjs",
|
||||||
|
"default": "./runtime/client.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/library": {
|
||||||
|
"types": "./runtime/library.d.ts",
|
||||||
|
"require": "./runtime/library.js",
|
||||||
|
"import": "./runtime/library.mjs",
|
||||||
|
"default": "./runtime/library.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/binary": {
|
||||||
|
"types": "./runtime/binary.d.ts",
|
||||||
|
"require": "./runtime/binary.js",
|
||||||
|
"import": "./runtime/binary.mjs",
|
||||||
|
"default": "./runtime/binary.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/wasm-engine-edge": {
|
||||||
|
"types": "./runtime/wasm-engine-edge.d.ts",
|
||||||
|
"require": "./runtime/wasm-engine-edge.js",
|
||||||
|
"import": "./runtime/wasm-engine-edge.mjs",
|
||||||
|
"default": "./runtime/wasm-engine-edge.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/wasm-compiler-edge": {
|
||||||
|
"types": "./runtime/wasm-compiler-edge.d.ts",
|
||||||
|
"require": "./runtime/wasm-compiler-edge.js",
|
||||||
|
"import": "./runtime/wasm-compiler-edge.mjs",
|
||||||
|
"default": "./runtime/wasm-compiler-edge.mjs"
|
||||||
|
},
|
||||||
|
"./runtime/edge": {
|
||||||
|
"types": "./runtime/edge.d.ts",
|
||||||
|
"require": "./runtime/edge.js",
|
||||||
|
"import": "./runtime/edge-esm.js",
|
||||||
|
"default": "./runtime/edge-esm.js"
|
||||||
|
},
|
||||||
|
"./runtime/react-native": {
|
||||||
|
"types": "./runtime/react-native.d.ts",
|
||||||
|
"require": "./runtime/react-native.js",
|
||||||
|
"import": "./runtime/react-native.js",
|
||||||
|
"default": "./runtime/react-native.js"
|
||||||
|
},
|
||||||
|
"./runtime/index-browser": {
|
||||||
|
"types": "./runtime/index-browser.d.ts",
|
||||||
|
"require": "./runtime/index-browser.js",
|
||||||
|
"import": "./runtime/index-browser.mjs",
|
||||||
|
"default": "./runtime/index-browser.mjs"
|
||||||
|
},
|
||||||
|
"./generator-build": {
|
||||||
|
"require": "./generator-build/index.js",
|
||||||
|
"import": "./generator-build/index.js",
|
||||||
|
"default": "./generator-build/index.js"
|
||||||
|
},
|
||||||
|
"./sql": {
|
||||||
|
"require": {
|
||||||
|
"types": "./sql.d.ts",
|
||||||
|
"node": "./sql.js",
|
||||||
|
"default": "./sql.js"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"types": "./sql.d.ts",
|
||||||
|
"node": "./sql.mjs",
|
||||||
|
"default": "./sql.mjs"
|
||||||
|
},
|
||||||
|
"default": "./sql.js"
|
||||||
|
},
|
||||||
|
"./*": "./*"
|
||||||
|
},
|
||||||
|
"version": "6.18.0",
|
||||||
|
"sideEffects": false,
|
||||||
|
"imports": {
|
||||||
|
"#wasm-engine-loader": {
|
||||||
|
"edge-light": "./wasm-edge-light-loader.mjs",
|
||||||
|
"workerd": "./wasm-worker-loader.mjs",
|
||||||
|
"worker": "./wasm-worker-loader.mjs",
|
||||||
|
"default": "./wasm-worker-loader.mjs"
|
||||||
|
},
|
||||||
|
"#main-entry-point": {
|
||||||
|
"require": {
|
||||||
|
"node": "./index.js",
|
||||||
|
"edge-light": "./wasm.js",
|
||||||
|
"workerd": "./wasm.js",
|
||||||
|
"worker": "./wasm.js",
|
||||||
|
"browser": "./index-browser.js",
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"node": "./index.js",
|
||||||
|
"edge-light": "./wasm.js",
|
||||||
|
"workerd": "./wasm.js",
|
||||||
|
"worker": "./wasm.js",
|
||||||
|
"browser": "./index-browser.js",
|
||||||
|
"default": "./index.js"
|
||||||
|
},
|
||||||
|
"default": "./index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/generated/prisma/query_engine-windows.dll.node
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node
Normal file
Binary file not shown.
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp12336
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp12336
Normal file
Binary file not shown.
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp22464
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp22464
Normal file
Binary file not shown.
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp3292
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp3292
Normal file
Binary file not shown.
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp396
Normal file
BIN
src/generated/prisma/query_engine-windows.dll.node.tmp396
Normal file
Binary file not shown.
2
src/generated/prisma/query_engine_bg.js
Normal file
2
src/generated/prisma/query_engine_bg.js
Normal file
File diff suppressed because one or more lines are too long
BIN
src/generated/prisma/query_engine_bg.wasm
Normal file
BIN
src/generated/prisma/query_engine_bg.wasm
Normal file
Binary file not shown.
34
src/generated/prisma/runtime/edge-esm.js
Normal file
34
src/generated/prisma/runtime/edge-esm.js
Normal file
File diff suppressed because one or more lines are too long
34
src/generated/prisma/runtime/edge.js
Normal file
34
src/generated/prisma/runtime/edge.js
Normal file
File diff suppressed because one or more lines are too long
370
src/generated/prisma/runtime/index-browser.d.ts
vendored
Normal file
370
src/generated/prisma/runtime/index-browser.d.ts
vendored
Normal file
|
|
@ -0,0 +1,370 @@
|
||||||
|
declare class AnyNull extends NullTypesEnumValue {
|
||||||
|
#private;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type Args<T, F extends Operation> = T extends {
|
||||||
|
[K: symbol]: {
|
||||||
|
types: {
|
||||||
|
operations: {
|
||||||
|
[K in F]: {
|
||||||
|
args: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} ? T[symbol]['types']['operations'][F]['args'] : any;
|
||||||
|
|
||||||
|
declare class DbNull extends NullTypesEnumValue {
|
||||||
|
#private;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare function Decimal(n: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
export declare namespace Decimal {
|
||||||
|
export type Constructor = typeof Decimal;
|
||||||
|
export type Instance = Decimal;
|
||||||
|
export type Rounding = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
||||||
|
export type Modulo = Rounding | 9;
|
||||||
|
export type Value = string | number | Decimal;
|
||||||
|
|
||||||
|
// http://mikemcl.github.io/decimal.js/#constructor-properties
|
||||||
|
export interface Config {
|
||||||
|
precision?: number;
|
||||||
|
rounding?: Rounding;
|
||||||
|
toExpNeg?: number;
|
||||||
|
toExpPos?: number;
|
||||||
|
minE?: number;
|
||||||
|
maxE?: number;
|
||||||
|
crypto?: boolean;
|
||||||
|
modulo?: Modulo;
|
||||||
|
defaults?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare class Decimal {
|
||||||
|
readonly d: number[];
|
||||||
|
readonly e: number;
|
||||||
|
readonly s: number;
|
||||||
|
|
||||||
|
constructor(n: Decimal.Value);
|
||||||
|
|
||||||
|
absoluteValue(): Decimal;
|
||||||
|
abs(): Decimal;
|
||||||
|
|
||||||
|
ceil(): Decimal;
|
||||||
|
|
||||||
|
clampedTo(min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||||
|
clamp(min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
comparedTo(n: Decimal.Value): number;
|
||||||
|
cmp(n: Decimal.Value): number;
|
||||||
|
|
||||||
|
cosine(): Decimal;
|
||||||
|
cos(): Decimal;
|
||||||
|
|
||||||
|
cubeRoot(): Decimal;
|
||||||
|
cbrt(): Decimal;
|
||||||
|
|
||||||
|
decimalPlaces(): number;
|
||||||
|
dp(): number;
|
||||||
|
|
||||||
|
dividedBy(n: Decimal.Value): Decimal;
|
||||||
|
div(n: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
dividedToIntegerBy(n: Decimal.Value): Decimal;
|
||||||
|
divToInt(n: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
equals(n: Decimal.Value): boolean;
|
||||||
|
eq(n: Decimal.Value): boolean;
|
||||||
|
|
||||||
|
floor(): Decimal;
|
||||||
|
|
||||||
|
greaterThan(n: Decimal.Value): boolean;
|
||||||
|
gt(n: Decimal.Value): boolean;
|
||||||
|
|
||||||
|
greaterThanOrEqualTo(n: Decimal.Value): boolean;
|
||||||
|
gte(n: Decimal.Value): boolean;
|
||||||
|
|
||||||
|
hyperbolicCosine(): Decimal;
|
||||||
|
cosh(): Decimal;
|
||||||
|
|
||||||
|
hyperbolicSine(): Decimal;
|
||||||
|
sinh(): Decimal;
|
||||||
|
|
||||||
|
hyperbolicTangent(): Decimal;
|
||||||
|
tanh(): Decimal;
|
||||||
|
|
||||||
|
inverseCosine(): Decimal;
|
||||||
|
acos(): Decimal;
|
||||||
|
|
||||||
|
inverseHyperbolicCosine(): Decimal;
|
||||||
|
acosh(): Decimal;
|
||||||
|
|
||||||
|
inverseHyperbolicSine(): Decimal;
|
||||||
|
asinh(): Decimal;
|
||||||
|
|
||||||
|
inverseHyperbolicTangent(): Decimal;
|
||||||
|
atanh(): Decimal;
|
||||||
|
|
||||||
|
inverseSine(): Decimal;
|
||||||
|
asin(): Decimal;
|
||||||
|
|
||||||
|
inverseTangent(): Decimal;
|
||||||
|
atan(): Decimal;
|
||||||
|
|
||||||
|
isFinite(): boolean;
|
||||||
|
|
||||||
|
isInteger(): boolean;
|
||||||
|
isInt(): boolean;
|
||||||
|
|
||||||
|
isNaN(): boolean;
|
||||||
|
|
||||||
|
isNegative(): boolean;
|
||||||
|
isNeg(): boolean;
|
||||||
|
|
||||||
|
isPositive(): boolean;
|
||||||
|
isPos(): boolean;
|
||||||
|
|
||||||
|
isZero(): boolean;
|
||||||
|
|
||||||
|
lessThan(n: Decimal.Value): boolean;
|
||||||
|
lt(n: Decimal.Value): boolean;
|
||||||
|
|
||||||
|
lessThanOrEqualTo(n: Decimal.Value): boolean;
|
||||||
|
lte(n: Decimal.Value): boolean;
|
||||||
|
|
||||||
|
logarithm(n?: Decimal.Value): Decimal;
|
||||||
|
log(n?: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
minus(n: Decimal.Value): Decimal;
|
||||||
|
sub(n: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
modulo(n: Decimal.Value): Decimal;
|
||||||
|
mod(n: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
naturalExponential(): Decimal;
|
||||||
|
exp(): Decimal;
|
||||||
|
|
||||||
|
naturalLogarithm(): Decimal;
|
||||||
|
ln(): Decimal;
|
||||||
|
|
||||||
|
negated(): Decimal;
|
||||||
|
neg(): Decimal;
|
||||||
|
|
||||||
|
plus(n: Decimal.Value): Decimal;
|
||||||
|
add(n: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
precision(includeZeros?: boolean): number;
|
||||||
|
sd(includeZeros?: boolean): number;
|
||||||
|
|
||||||
|
round(): Decimal;
|
||||||
|
|
||||||
|
sine() : Decimal;
|
||||||
|
sin() : Decimal;
|
||||||
|
|
||||||
|
squareRoot(): Decimal;
|
||||||
|
sqrt(): Decimal;
|
||||||
|
|
||||||
|
tangent() : Decimal;
|
||||||
|
tan() : Decimal;
|
||||||
|
|
||||||
|
times(n: Decimal.Value): Decimal;
|
||||||
|
mul(n: Decimal.Value) : Decimal;
|
||||||
|
|
||||||
|
toBinary(significantDigits?: number): string;
|
||||||
|
toBinary(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||||
|
|
||||||
|
toDecimalPlaces(decimalPlaces?: number): Decimal;
|
||||||
|
toDecimalPlaces(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
|
||||||
|
toDP(decimalPlaces?: number): Decimal;
|
||||||
|
toDP(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
|
||||||
|
|
||||||
|
toExponential(decimalPlaces?: number): string;
|
||||||
|
toExponential(decimalPlaces: number, rounding: Decimal.Rounding): string;
|
||||||
|
|
||||||
|
toFixed(decimalPlaces?: number): string;
|
||||||
|
toFixed(decimalPlaces: number, rounding: Decimal.Rounding): string;
|
||||||
|
|
||||||
|
toFraction(max_denominator?: Decimal.Value): Decimal[];
|
||||||
|
|
||||||
|
toHexadecimal(significantDigits?: number): string;
|
||||||
|
toHexadecimal(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||||
|
toHex(significantDigits?: number): string;
|
||||||
|
toHex(significantDigits: number, rounding?: Decimal.Rounding): string;
|
||||||
|
|
||||||
|
toJSON(): string;
|
||||||
|
|
||||||
|
toNearest(n: Decimal.Value, rounding?: Decimal.Rounding): Decimal;
|
||||||
|
|
||||||
|
toNumber(): number;
|
||||||
|
|
||||||
|
toOctal(significantDigits?: number): string;
|
||||||
|
toOctal(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||||
|
|
||||||
|
toPower(n: Decimal.Value): Decimal;
|
||||||
|
pow(n: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
toPrecision(significantDigits?: number): string;
|
||||||
|
toPrecision(significantDigits: number, rounding: Decimal.Rounding): string;
|
||||||
|
|
||||||
|
toSignificantDigits(significantDigits?: number): Decimal;
|
||||||
|
toSignificantDigits(significantDigits: number, rounding: Decimal.Rounding): Decimal;
|
||||||
|
toSD(significantDigits?: number): Decimal;
|
||||||
|
toSD(significantDigits: number, rounding: Decimal.Rounding): Decimal;
|
||||||
|
|
||||||
|
toString(): string;
|
||||||
|
|
||||||
|
truncated(): Decimal;
|
||||||
|
trunc(): Decimal;
|
||||||
|
|
||||||
|
valueOf(): string;
|
||||||
|
|
||||||
|
static abs(n: Decimal.Value): Decimal;
|
||||||
|
static acos(n: Decimal.Value): Decimal;
|
||||||
|
static acosh(n: Decimal.Value): Decimal;
|
||||||
|
static add(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||||
|
static asin(n: Decimal.Value): Decimal;
|
||||||
|
static asinh(n: Decimal.Value): Decimal;
|
||||||
|
static atan(n: Decimal.Value): Decimal;
|
||||||
|
static atanh(n: Decimal.Value): Decimal;
|
||||||
|
static atan2(y: Decimal.Value, x: Decimal.Value): Decimal;
|
||||||
|
static cbrt(n: Decimal.Value): Decimal;
|
||||||
|
static ceil(n: Decimal.Value): Decimal;
|
||||||
|
static clamp(n: Decimal.Value, min: Decimal.Value, max: Decimal.Value): Decimal;
|
||||||
|
static clone(object?: Decimal.Config): Decimal.Constructor;
|
||||||
|
static config(object: Decimal.Config): Decimal.Constructor;
|
||||||
|
static cos(n: Decimal.Value): Decimal;
|
||||||
|
static cosh(n: Decimal.Value): Decimal;
|
||||||
|
static div(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||||
|
static exp(n: Decimal.Value): Decimal;
|
||||||
|
static floor(n: Decimal.Value): Decimal;
|
||||||
|
static hypot(...n: Decimal.Value[]): Decimal;
|
||||||
|
static isDecimal(object: any): object is Decimal;
|
||||||
|
static ln(n: Decimal.Value): Decimal;
|
||||||
|
static log(n: Decimal.Value, base?: Decimal.Value): Decimal;
|
||||||
|
static log2(n: Decimal.Value): Decimal;
|
||||||
|
static log10(n: Decimal.Value): Decimal;
|
||||||
|
static max(...n: Decimal.Value[]): Decimal;
|
||||||
|
static min(...n: Decimal.Value[]): Decimal;
|
||||||
|
static mod(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||||
|
static mul(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||||
|
static noConflict(): Decimal.Constructor; // Browser only
|
||||||
|
static pow(base: Decimal.Value, exponent: Decimal.Value): Decimal;
|
||||||
|
static random(significantDigits?: number): Decimal;
|
||||||
|
static round(n: Decimal.Value): Decimal;
|
||||||
|
static set(object: Decimal.Config): Decimal.Constructor;
|
||||||
|
static sign(n: Decimal.Value): number;
|
||||||
|
static sin(n: Decimal.Value): Decimal;
|
||||||
|
static sinh(n: Decimal.Value): Decimal;
|
||||||
|
static sqrt(n: Decimal.Value): Decimal;
|
||||||
|
static sub(x: Decimal.Value, y: Decimal.Value): Decimal;
|
||||||
|
static sum(...n: Decimal.Value[]): Decimal;
|
||||||
|
static tan(n: Decimal.Value): Decimal;
|
||||||
|
static tanh(n: Decimal.Value): Decimal;
|
||||||
|
static trunc(n: Decimal.Value): Decimal;
|
||||||
|
|
||||||
|
static readonly default?: Decimal.Constructor;
|
||||||
|
static readonly Decimal?: Decimal.Constructor;
|
||||||
|
|
||||||
|
static readonly precision: number;
|
||||||
|
static readonly rounding: Decimal.Rounding;
|
||||||
|
static readonly toExpNeg: number;
|
||||||
|
static readonly toExpPos: number;
|
||||||
|
static readonly minE: number;
|
||||||
|
static readonly maxE: number;
|
||||||
|
static readonly crypto: boolean;
|
||||||
|
static readonly modulo: Decimal.Modulo;
|
||||||
|
|
||||||
|
static readonly ROUND_UP: 0;
|
||||||
|
static readonly ROUND_DOWN: 1;
|
||||||
|
static readonly ROUND_CEIL: 2;
|
||||||
|
static readonly ROUND_FLOOR: 3;
|
||||||
|
static readonly ROUND_HALF_UP: 4;
|
||||||
|
static readonly ROUND_HALF_DOWN: 5;
|
||||||
|
static readonly ROUND_HALF_EVEN: 6;
|
||||||
|
static readonly ROUND_HALF_CEIL: 7;
|
||||||
|
static readonly ROUND_HALF_FLOOR: 8;
|
||||||
|
static readonly EUCLID: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type Exact<A, W> = (A extends unknown ? (W extends A ? {
|
||||||
|
[K in keyof A]: Exact<A[K], W[K]>;
|
||||||
|
} : W) : never) | (A extends Narrowable ? A : never);
|
||||||
|
|
||||||
|
export declare function getRuntime(): GetRuntimeOutput;
|
||||||
|
|
||||||
|
declare type GetRuntimeOutput = {
|
||||||
|
id: RuntimeName;
|
||||||
|
prettyName: string;
|
||||||
|
isEdge: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare class JsonNull extends NullTypesEnumValue {
|
||||||
|
#private;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates more strict variant of an enum which, unlike regular enum,
|
||||||
|
* throws on non-existing property access. This can be useful in following situations:
|
||||||
|
* - we have an API, that accepts both `undefined` and `SomeEnumType` as an input
|
||||||
|
* - enum values are generated dynamically from DMMF.
|
||||||
|
*
|
||||||
|
* In that case, if using normal enums and no compile-time typechecking, using non-existing property
|
||||||
|
* will result in `undefined` value being used, which will be accepted. Using strict enum
|
||||||
|
* in this case will help to have a runtime exception, telling you that you are probably doing something wrong.
|
||||||
|
*
|
||||||
|
* Note: if you need to check for existence of a value in the enum you can still use either
|
||||||
|
* `in` operator or `hasOwnProperty` function.
|
||||||
|
*
|
||||||
|
* @param definition
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export declare function makeStrictEnum<T extends Record<PropertyKey, string | number>>(definition: T): T;
|
||||||
|
|
||||||
|
declare type Narrowable = string | number | bigint | boolean | [];
|
||||||
|
|
||||||
|
declare class NullTypesEnumValue extends ObjectEnumValue {
|
||||||
|
_getNamespace(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for unique values of object-valued enums.
|
||||||
|
*/
|
||||||
|
declare abstract class ObjectEnumValue {
|
||||||
|
constructor(arg?: symbol);
|
||||||
|
abstract _getNamespace(): string;
|
||||||
|
_getName(): string;
|
||||||
|
toString(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare const objectEnumValues: {
|
||||||
|
classes: {
|
||||||
|
DbNull: typeof DbNull;
|
||||||
|
JsonNull: typeof JsonNull;
|
||||||
|
AnyNull: typeof AnyNull;
|
||||||
|
};
|
||||||
|
instances: {
|
||||||
|
DbNull: DbNull;
|
||||||
|
JsonNull: JsonNull;
|
||||||
|
AnyNull: AnyNull;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
declare type Operation = 'findFirst' | 'findFirstOrThrow' | 'findUnique' | 'findUniqueOrThrow' | 'findMany' | 'create' | 'createMany' | 'createManyAndReturn' | 'update' | 'updateMany' | 'updateManyAndReturn' | 'upsert' | 'delete' | 'deleteMany' | 'aggregate' | 'count' | 'groupBy' | '$queryRaw' | '$executeRaw' | '$queryRawUnsafe' | '$executeRawUnsafe' | 'findRaw' | 'aggregateRaw' | '$runCommandRaw';
|
||||||
|
|
||||||
|
declare namespace Public {
|
||||||
|
export {
|
||||||
|
validator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export { Public }
|
||||||
|
|
||||||
|
declare type RuntimeName = 'workerd' | 'deno' | 'netlify' | 'node' | 'bun' | 'edge-light' | '';
|
||||||
|
|
||||||
|
declare function validator<V>(): <S>(select: Exact<S, V>) => S;
|
||||||
|
|
||||||
|
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation>(client: C, model: M, operation: O): <S>(select: Exact<S, Args<C[M], O>>) => S;
|
||||||
|
|
||||||
|
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation, P extends keyof Args<C[M], O>>(client: C, model: M, operation: O, prop: P): <S>(select: Exact<S, Args<C[M], O>[P]>) => S;
|
||||||
|
|
||||||
|
export { }
|
||||||
16
src/generated/prisma/runtime/index-browser.js
Normal file
16
src/generated/prisma/runtime/index-browser.js
Normal file
File diff suppressed because one or more lines are too long
3982
src/generated/prisma/runtime/library.d.ts
vendored
Normal file
3982
src/generated/prisma/runtime/library.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
146
src/generated/prisma/runtime/library.js
Normal file
146
src/generated/prisma/runtime/library.js
Normal file
File diff suppressed because one or more lines are too long
83
src/generated/prisma/runtime/react-native.js
vendored
Normal file
83
src/generated/prisma/runtime/react-native.js
vendored
Normal file
File diff suppressed because one or more lines are too long
84
src/generated/prisma/runtime/wasm-compiler-edge.js
Normal file
84
src/generated/prisma/runtime/wasm-compiler-edge.js
Normal file
File diff suppressed because one or more lines are too long
36
src/generated/prisma/runtime/wasm-engine-edge.js
Normal file
36
src/generated/prisma/runtime/wasm-engine-edge.js
Normal file
File diff suppressed because one or more lines are too long
48
src/generated/prisma/schema.prisma
Normal file
48
src/generated/prisma/schema.prisma
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
output = "../src/generated/prisma"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Modello {
|
||||||
|
FORM100
|
||||||
|
FORM200
|
||||||
|
FORM200PLUS
|
||||||
|
FORM500
|
||||||
|
}
|
||||||
|
|
||||||
|
model Intervento {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
id_registratore String
|
||||||
|
data DateTime @db.Date
|
||||||
|
lavoro String @db.Text
|
||||||
|
fattura Boolean @db.Boolean
|
||||||
|
registratore Registratore @relation(fields: [id_registratore], references: [id])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Registratore {
|
||||||
|
id String @id
|
||||||
|
id_cliente Int
|
||||||
|
modello Modello?
|
||||||
|
data_acquisto DateTime? @db.Date
|
||||||
|
ultima_verifica DateTime? @db.Date
|
||||||
|
prossima_verifica DateTime? @db.Date
|
||||||
|
cliente Cliente @relation(fields: [id_cliente], references: [id])
|
||||||
|
interventi Intervento[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Cliente {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
ragione_sociale String @unique @db.VarChar(255)
|
||||||
|
email String @db.VarChar(255)
|
||||||
|
partita_iva String @db.VarChar(255)
|
||||||
|
telefono String @db.VarChar(255)
|
||||||
|
sede String @db.VarChar(255)
|
||||||
|
sede_url String @db.VarChar(255)
|
||||||
|
contratto String @db.VarChar(255)
|
||||||
|
registratori Registratore[]
|
||||||
|
}
|
||||||
4
src/generated/prisma/wasm-edge-light-loader.mjs
Normal file
4
src/generated/prisma/wasm-edge-light-loader.mjs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||||
|
/* eslint-disable */
|
||||||
|
export default import('./query_engine_bg.wasm?module')
|
||||||
4
src/generated/prisma/wasm-worker-loader.mjs
Normal file
4
src/generated/prisma/wasm-worker-loader.mjs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||||
|
/* eslint-disable */
|
||||||
|
export default import('./query_engine_bg.wasm')
|
||||||
1
src/generated/prisma/wasm.d.ts
vendored
Normal file
1
src/generated/prisma/wasm.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./default"
|
||||||
229
src/generated/prisma/wasm.js
Normal file
229
src/generated/prisma/wasm.js
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
|
||||||
|
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
|
||||||
|
const {
|
||||||
|
PrismaClientKnownRequestError,
|
||||||
|
PrismaClientUnknownRequestError,
|
||||||
|
PrismaClientRustPanicError,
|
||||||
|
PrismaClientInitializationError,
|
||||||
|
PrismaClientValidationError,
|
||||||
|
getPrismaClient,
|
||||||
|
sqltag,
|
||||||
|
empty,
|
||||||
|
join,
|
||||||
|
raw,
|
||||||
|
skip,
|
||||||
|
Decimal,
|
||||||
|
Debug,
|
||||||
|
objectEnumValues,
|
||||||
|
makeStrictEnum,
|
||||||
|
Extensions,
|
||||||
|
warnOnce,
|
||||||
|
defineDmmfProperty,
|
||||||
|
Public,
|
||||||
|
getRuntime,
|
||||||
|
createParam,
|
||||||
|
} = require('./runtime/wasm-engine-edge.js')
|
||||||
|
|
||||||
|
|
||||||
|
const Prisma = {}
|
||||||
|
|
||||||
|
exports.Prisma = Prisma
|
||||||
|
exports.$Enums = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prisma Client JS version: 6.18.0
|
||||||
|
* Query Engine version: 34b5a692b7bd79939a9a2c3ef97d816e749cda2f
|
||||||
|
*/
|
||||||
|
Prisma.prismaVersion = {
|
||||||
|
client: "6.18.0",
|
||||||
|
engine: "34b5a692b7bd79939a9a2c3ef97d816e749cda2f"
|
||||||
|
}
|
||||||
|
|
||||||
|
Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
|
||||||
|
Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
|
||||||
|
Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
|
||||||
|
Prisma.PrismaClientInitializationError = PrismaClientInitializationError
|
||||||
|
Prisma.PrismaClientValidationError = PrismaClientValidationError
|
||||||
|
Prisma.Decimal = Decimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-export of sql-template-tag
|
||||||
|
*/
|
||||||
|
Prisma.sql = sqltag
|
||||||
|
Prisma.empty = empty
|
||||||
|
Prisma.join = join
|
||||||
|
Prisma.raw = raw
|
||||||
|
Prisma.validator = Public.validator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extensions
|
||||||
|
*/
|
||||||
|
Prisma.getExtensionContext = Extensions.getExtensionContext
|
||||||
|
Prisma.defineExtension = Extensions.defineExtension
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthand utilities for JSON filtering
|
||||||
|
*/
|
||||||
|
Prisma.DbNull = objectEnumValues.instances.DbNull
|
||||||
|
Prisma.JsonNull = objectEnumValues.instances.JsonNull
|
||||||
|
Prisma.AnyNull = objectEnumValues.instances.AnyNull
|
||||||
|
|
||||||
|
Prisma.NullTypes = {
|
||||||
|
DbNull: objectEnumValues.classes.DbNull,
|
||||||
|
JsonNull: objectEnumValues.classes.JsonNull,
|
||||||
|
AnyNull: objectEnumValues.classes.AnyNull
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enums
|
||||||
|
*/
|
||||||
|
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||||
|
ReadUncommitted: 'ReadUncommitted',
|
||||||
|
ReadCommitted: 'ReadCommitted',
|
||||||
|
RepeatableRead: 'RepeatableRead',
|
||||||
|
Serializable: 'Serializable'
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.Prisma.InterventoScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
id_registratore: 'id_registratore',
|
||||||
|
data: 'data',
|
||||||
|
lavoro: 'lavoro',
|
||||||
|
fattura: 'fattura'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.RegistratoreScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
id_cliente: 'id_cliente',
|
||||||
|
modello: 'modello',
|
||||||
|
data_acquisto: 'data_acquisto',
|
||||||
|
ultima_verifica: 'ultima_verifica',
|
||||||
|
prossima_verifica: 'prossima_verifica'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.ClienteScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
ragione_sociale: 'ragione_sociale',
|
||||||
|
email: 'email',
|
||||||
|
partita_iva: 'partita_iva',
|
||||||
|
telefono: 'telefono',
|
||||||
|
sede: 'sede',
|
||||||
|
sede_url: 'sede_url',
|
||||||
|
contratto: 'contratto',
|
||||||
|
lat: 'lat',
|
||||||
|
lon: 'lon'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.SortOrder = {
|
||||||
|
asc: 'asc',
|
||||||
|
desc: 'desc'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.QueryMode = {
|
||||||
|
default: 'default',
|
||||||
|
insensitive: 'insensitive'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
};
|
||||||
|
exports.Modello = exports.$Enums.Modello = {
|
||||||
|
FORM100: 'FORM100',
|
||||||
|
FORM200: 'FORM200',
|
||||||
|
FORM200PLUS: 'FORM200PLUS',
|
||||||
|
FORM500: 'FORM500'
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Prisma.ModelName = {
|
||||||
|
Intervento: 'Intervento',
|
||||||
|
Registratore: 'Registratore',
|
||||||
|
Cliente: 'Cliente'
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Create the Client
|
||||||
|
*/
|
||||||
|
const config = {
|
||||||
|
"generator": {
|
||||||
|
"name": "client",
|
||||||
|
"provider": {
|
||||||
|
"fromEnvVar": null,
|
||||||
|
"value": "prisma-client-js"
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"value": "C:\\Users\\nicola\\Documents\\dev\\dash-registratori\\src\\generated\\prisma",
|
||||||
|
"fromEnvVar": null
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"engineType": "library"
|
||||||
|
},
|
||||||
|
"binaryTargets": [
|
||||||
|
{
|
||||||
|
"fromEnvVar": null,
|
||||||
|
"value": "windows",
|
||||||
|
"native": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"previewFeatures": [],
|
||||||
|
"sourceFilePath": "C:\\Users\\nicola\\Documents\\dev\\dash-registratori\\prisma\\schema.prisma",
|
||||||
|
"isCustomOutput": true
|
||||||
|
},
|
||||||
|
"relativeEnvPaths": {
|
||||||
|
"rootEnvPath": null,
|
||||||
|
"schemaEnvPath": "../../../.env"
|
||||||
|
},
|
||||||
|
"relativePath": "../../../prisma",
|
||||||
|
"clientVersion": "6.18.0",
|
||||||
|
"engineVersion": "34b5a692b7bd79939a9a2c3ef97d816e749cda2f",
|
||||||
|
"datasourceNames": [
|
||||||
|
"db"
|
||||||
|
],
|
||||||
|
"activeProvider": "postgresql",
|
||||||
|
"inlineDatasources": {
|
||||||
|
"db": {
|
||||||
|
"url": {
|
||||||
|
"fromEnvVar": "DATABASE_URL",
|
||||||
|
"value": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inlineSchema": "generator client {\n provider = \"prisma-client-js\"\n output = \"../src/generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\nenum Modello {\n FORM100\n FORM200\n FORM200PLUS\n FORM500\n}\n\nmodel Intervento {\n id Int @id @default(autoincrement())\n id_registratore String\n data DateTime @db.Date\n lavoro String @db.Text\n fattura Boolean @db.Boolean\n registratore Registratore @relation(fields: [id_registratore], references: [id])\n}\n\nmodel Registratore {\n id String @id\n id_cliente Int\n modello Modello?\n data_acquisto DateTime? @db.Date\n ultima_verifica DateTime? @db.Date\n prossima_verifica DateTime? @db.Date\n cliente Cliente @relation(fields: [id_cliente], references: [id])\n interventi Intervento[]\n}\n\nmodel Cliente {\n id Int @id @default(autoincrement())\n ragione_sociale String @unique @db.VarChar(255)\n email String @db.VarChar(255)\n partita_iva String @db.VarChar(255)\n telefono String @db.VarChar(255)\n sede String @db.VarChar(255)\n sede_url String\n contratto String @db.VarChar(255)\n lat Float\n lon Float\n registratori Registratore[]\n}\n",
|
||||||
|
"inlineSchemaHash": "9bd9d1485d1021de6ddd08a2040ad878357151b96b361e965ef2ca00c0bca296",
|
||||||
|
"copyEngine": true
|
||||||
|
}
|
||||||
|
config.dirname = '/'
|
||||||
|
|
||||||
|
config.runtimeDataModel = JSON.parse("{\"models\":{\"Intervento\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"id_registratore\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"data\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"lavoro\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"fattura\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"registratore\",\"kind\":\"object\",\"type\":\"Registratore\",\"relationName\":\"InterventoToRegistratore\"}],\"dbName\":null},\"Registratore\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"id_cliente\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"modello\",\"kind\":\"enum\",\"type\":\"Modello\"},{\"name\":\"data_acquisto\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"ultima_verifica\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"prossima_verifica\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"cliente\",\"kind\":\"object\",\"type\":\"Cliente\",\"relationName\":\"ClienteToRegistratore\"},{\"name\":\"interventi\",\"kind\":\"object\",\"type\":\"Intervento\",\"relationName\":\"InterventoToRegistratore\"}],\"dbName\":null},\"Cliente\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"ragione_sociale\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"partita_iva\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"telefono\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sede\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"sede_url\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"contratto\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lat\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"lon\",\"kind\":\"scalar\",\"type\":\"Float\"},{\"name\":\"registratori\",\"kind\":\"object\",\"type\":\"Registratore\",\"relationName\":\"ClienteToRegistratore\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
||||||
|
defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
|
||||||
|
config.engineWasm = {
|
||||||
|
getRuntime: async () => require('./query_engine_bg.js'),
|
||||||
|
getQueryEngineWasmModule: async () => {
|
||||||
|
const loader = (await import('#wasm-engine-loader')).default
|
||||||
|
const engine = (await loader).default
|
||||||
|
return engine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.compilerWasm = undefined
|
||||||
|
|
||||||
|
config.injectableEdgeEnv = () => ({
|
||||||
|
parsed: {
|
||||||
|
DATABASE_URL: typeof globalThis !== 'undefined' && globalThis['DATABASE_URL'] || typeof process !== 'undefined' && process.env && process.env.DATABASE_URL || undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined) {
|
||||||
|
Debug.enable(typeof globalThis !== 'undefined' && globalThis['DEBUG'] || typeof process !== 'undefined' && process.env && process.env.DEBUG || undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
const PrismaClient = getPrismaClient(config)
|
||||||
|
exports.PrismaClient = PrismaClient
|
||||||
|
Object.assign(exports, Prisma)
|
||||||
|
|
||||||
Loading…
Reference in a new issue