feat - new schemas
This commit is contained in:
parent
970c8b33db
commit
96ebac5547
@ -1,44 +1,131 @@
|
|||||||
|
import { sql } from "drizzle-orm"
|
||||||
|
import {
|
||||||
|
doublePrecision,
|
||||||
|
foreignKey,
|
||||||
|
integer,
|
||||||
|
pgPolicy,
|
||||||
|
pgTable,
|
||||||
|
serial,
|
||||||
|
text,
|
||||||
|
uuid,
|
||||||
|
varchar
|
||||||
|
} from "drizzle-orm/pg-core"
|
||||||
|
import { authenticatedRole, authUsers } from "drizzle-orm/supabase"
|
||||||
|
|
||||||
|
export const users = pgTable("demo", {
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
import { sql } from 'drizzle-orm';
|
fullName: text("full_name"),
|
||||||
import { foreignKey, pgPolicy, pgTable, serial, text, uuid, varchar } from 'drizzle-orm/pg-core';
|
phone: varchar("phone", { length: 256 })
|
||||||
import { authenticatedRole, authUsers } from 'drizzle-orm/supabase';
|
})
|
||||||
|
|
||||||
export const users = pgTable('demo', {
|
|
||||||
id: serial('id').primaryKey(),
|
|
||||||
fullName: text('full_name'),
|
|
||||||
phone: varchar('phone', { length: 256 })
|
|
||||||
});
|
|
||||||
|
|
||||||
export const profiles = pgTable(
|
export const profiles = pgTable(
|
||||||
'profiles',
|
"profiles",
|
||||||
{
|
{
|
||||||
id: uuid('id').notNull().primaryKey(),
|
id: uuid("id").notNull().primaryKey(),
|
||||||
firstName: text('first_name'),
|
firstName: text("first_name"),
|
||||||
lastName: text('last_name'),
|
lastName: text("last_name")
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
foreignKey({
|
foreignKey({
|
||||||
columns: [table.id],
|
columns: [table.id],
|
||||||
foreignColumns: [authUsers.id],
|
foreignColumns: [authUsers.id],
|
||||||
name: 'profiles_id_fkey',
|
name: "profiles_id_fkey"
|
||||||
}).onDelete('cascade'),
|
}).onDelete("cascade"),
|
||||||
pgPolicy('select-own-profile', {
|
pgPolicy("select-own-profile", {
|
||||||
for: 'select',
|
for: "select",
|
||||||
|
to: authenticatedRole,
|
||||||
|
using: sql`${table.id} = auth.uid()`
|
||||||
|
}),
|
||||||
|
pgPolicy("update-own-profile", {
|
||||||
|
for: "update",
|
||||||
to: authenticatedRole,
|
to: authenticatedRole,
|
||||||
using: sql`${table.id} = auth.uid()`,
|
using: sql`${table.id} = auth.uid()`,
|
||||||
|
withCheck: sql`${table.id} = auth.uid()`
|
||||||
}),
|
}),
|
||||||
pgPolicy('update-own-profile', {
|
pgPolicy("insert-profile", {
|
||||||
for: 'update',
|
for: "insert",
|
||||||
to: authenticatedRole,
|
to: authenticatedRole,
|
||||||
using: sql`${table.id} = auth.uid()`,
|
withCheck: sql`${table.id} = auth.uid()`
|
||||||
withCheck: sql`${table.id} = auth.uid()`,
|
|
||||||
}),
|
|
||||||
pgPolicy('insert-profile', {
|
|
||||||
for: 'insert',
|
|
||||||
to: authenticatedRole,
|
|
||||||
withCheck: sql`${table.id} = auth.uid()`,
|
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
).enableRLS();
|
).enableRLS()
|
||||||
|
|
||||||
|
// === Catálogo de certificaciones ===
|
||||||
|
export const certifications = pgTable(
|
||||||
|
"certifications",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: text("name").notNull()
|
||||||
|
},
|
||||||
|
() => [
|
||||||
|
// Política: todos los usuarios autenticados pueden leer, nadie puede escribir
|
||||||
|
pgPolicy("select-certifications", {
|
||||||
|
for: "select",
|
||||||
|
to: authenticatedRole,
|
||||||
|
using: sql`true`
|
||||||
|
})
|
||||||
|
]
|
||||||
|
).enableRLS()
|
||||||
|
|
||||||
|
// === Catálogo de modelos de drones ===
|
||||||
|
export const droneModels = pgTable(
|
||||||
|
"drone_models",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: text("name").notNull()
|
||||||
|
},
|
||||||
|
() => [
|
||||||
|
pgPolicy("select-drone-models", {
|
||||||
|
for: "select",
|
||||||
|
to: authenticatedRole,
|
||||||
|
using: sql`true`
|
||||||
|
})
|
||||||
|
]
|
||||||
|
).enableRLS()
|
||||||
|
|
||||||
|
// === Tabla principal de pilotos ===
|
||||||
|
export const pilots = pgTable(
|
||||||
|
"pilots",
|
||||||
|
{
|
||||||
|
id: uuid("id").notNull().primaryKey(), // Igual que auth.uid()
|
||||||
|
name: text("name"),
|
||||||
|
location: text("location"),
|
||||||
|
latitude: doublePrecision("latitude"),
|
||||||
|
longitude: doublePrecision("longitude"),
|
||||||
|
company: text("company"),
|
||||||
|
position: text("position"),
|
||||||
|
description: text("description"),
|
||||||
|
differentiation: text("differentiation"),
|
||||||
|
coverageAreas: text("coverage_areas"),
|
||||||
|
specializationAreas: text("specialization_areas"),
|
||||||
|
certificationIds: integer("certification_ids").array(), // IDs de tabla certifications
|
||||||
|
droneModelIds: integer("drone_model_ids").array(), // IDs de tabla drone_models
|
||||||
|
email: text("email")
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
// Relación con la tabla auth.users
|
||||||
|
foreignKey({
|
||||||
|
columns: [table.id],
|
||||||
|
foreignColumns: [authUsers.id],
|
||||||
|
name: "pilots_id_fkey"
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
|
||||||
|
// === RLS ===
|
||||||
|
pgPolicy("select-own-pilot", {
|
||||||
|
for: "select",
|
||||||
|
to: authenticatedRole,
|
||||||
|
using: sql`${table.id} = auth.uid()`
|
||||||
|
}),
|
||||||
|
pgPolicy("update-own-pilot", {
|
||||||
|
for: "update",
|
||||||
|
to: authenticatedRole,
|
||||||
|
using: sql`${table.id} = auth.uid()`,
|
||||||
|
withCheck: sql`${table.id} = auth.uid()`
|
||||||
|
}),
|
||||||
|
pgPolicy("insert-pilot", {
|
||||||
|
for: "insert",
|
||||||
|
to: authenticatedRole,
|
||||||
|
withCheck: sql`${table.id} = auth.uid()`
|
||||||
|
})
|
||||||
|
]
|
||||||
|
).enableRLS()
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
CREATE TABLE "certifications" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"name" text NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "certifications" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
|
||||||
|
CREATE TABLE "drone_models" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"name" text NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "drone_models" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
|
||||||
|
CREATE TABLE "pilots" (
|
||||||
|
"id" uuid PRIMARY KEY NOT NULL,
|
||||||
|
"name" text,
|
||||||
|
"location" text,
|
||||||
|
"latitude" double precision,
|
||||||
|
"longitude" double precision,
|
||||||
|
"company" text,
|
||||||
|
"position" text,
|
||||||
|
"description" text,
|
||||||
|
"differentiation" text,
|
||||||
|
"coverage_areas" text,
|
||||||
|
"specialization_areas" text,
|
||||||
|
"certification_ids" integer[],
|
||||||
|
"drone_model_ids" integer[],
|
||||||
|
"email" text
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "pilots" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
|
||||||
|
ALTER TABLE "pilots" ADD CONSTRAINT "pilots_id_fkey" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||||
|
CREATE POLICY "select-certifications" ON "certifications" AS PERMISSIVE FOR SELECT TO "authenticated" USING (true);--> statement-breakpoint
|
||||||
|
CREATE POLICY "select-drone-models" ON "drone_models" AS PERMISSIVE FOR SELECT TO "authenticated" USING (true);--> statement-breakpoint
|
||||||
|
CREATE POLICY "select-own-pilot" ON "pilots" AS PERMISSIVE FOR SELECT TO "authenticated" USING ("pilots"."id" = auth.uid());--> statement-breakpoint
|
||||||
|
CREATE POLICY "update-own-pilot" ON "pilots" AS PERMISSIVE FOR UPDATE TO "authenticated" USING ("pilots"."id" = auth.uid()) WITH CHECK ("pilots"."id" = auth.uid());--> statement-breakpoint
|
||||||
|
CREATE POLICY "insert-pilot" ON "pilots" AS PERMISSIVE FOR INSERT TO "authenticated" WITH CHECK ("pilots"."id" = auth.uid());
|
||||||
336
src/integrations/supabase/migrations/meta/0002_snapshot.json
Normal file
336
src/integrations/supabase/migrations/meta/0002_snapshot.json
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
{
|
||||||
|
"id": "462e99b2-ba6b-4c91-9f6b-891795695955",
|
||||||
|
"prevId": "7d0d4272-65ba-45cf-9dd3-a5e2008d3744",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.certifications": {
|
||||||
|
"name": "certifications",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {
|
||||||
|
"select-certifications": {
|
||||||
|
"name": "select-certifications",
|
||||||
|
"as": "PERMISSIVE",
|
||||||
|
"for": "SELECT",
|
||||||
|
"to": [
|
||||||
|
"authenticated"
|
||||||
|
],
|
||||||
|
"using": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": true
|
||||||
|
},
|
||||||
|
"public.drone_models": {
|
||||||
|
"name": "drone_models",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {
|
||||||
|
"select-drone-models": {
|
||||||
|
"name": "select-drone-models",
|
||||||
|
"as": "PERMISSIVE",
|
||||||
|
"for": "SELECT",
|
||||||
|
"to": [
|
||||||
|
"authenticated"
|
||||||
|
],
|
||||||
|
"using": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": true
|
||||||
|
},
|
||||||
|
"public.pilots": {
|
||||||
|
"name": "pilots",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"name": "location",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"latitude": {
|
||||||
|
"name": "latitude",
|
||||||
|
"type": "double precision",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"longitude": {
|
||||||
|
"name": "longitude",
|
||||||
|
"type": "double precision",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"company": {
|
||||||
|
"name": "company",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"name": "position",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"differentiation": {
|
||||||
|
"name": "differentiation",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"coverage_areas": {
|
||||||
|
"name": "coverage_areas",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"specialization_areas": {
|
||||||
|
"name": "specialization_areas",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"certification_ids": {
|
||||||
|
"name": "certification_ids",
|
||||||
|
"type": "integer[]",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"drone_model_ids": {
|
||||||
|
"name": "drone_model_ids",
|
||||||
|
"type": "integer[]",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"pilots_id_fkey": {
|
||||||
|
"name": "pilots_id_fkey",
|
||||||
|
"tableFrom": "pilots",
|
||||||
|
"tableTo": "users",
|
||||||
|
"schemaTo": "auth",
|
||||||
|
"columnsFrom": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {
|
||||||
|
"select-own-pilot": {
|
||||||
|
"name": "select-own-pilot",
|
||||||
|
"as": "PERMISSIVE",
|
||||||
|
"for": "SELECT",
|
||||||
|
"to": [
|
||||||
|
"authenticated"
|
||||||
|
],
|
||||||
|
"using": "\"pilots\".\"id\" = auth.uid()"
|
||||||
|
},
|
||||||
|
"update-own-pilot": {
|
||||||
|
"name": "update-own-pilot",
|
||||||
|
"as": "PERMISSIVE",
|
||||||
|
"for": "UPDATE",
|
||||||
|
"to": [
|
||||||
|
"authenticated"
|
||||||
|
],
|
||||||
|
"using": "\"pilots\".\"id\" = auth.uid()",
|
||||||
|
"withCheck": "\"pilots\".\"id\" = auth.uid()"
|
||||||
|
},
|
||||||
|
"insert-pilot": {
|
||||||
|
"name": "insert-pilot",
|
||||||
|
"as": "PERMISSIVE",
|
||||||
|
"for": "INSERT",
|
||||||
|
"to": [
|
||||||
|
"authenticated"
|
||||||
|
],
|
||||||
|
"withCheck": "\"pilots\".\"id\" = auth.uid()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": true
|
||||||
|
},
|
||||||
|
"public.profiles": {
|
||||||
|
"name": "profiles",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"first_name": {
|
||||||
|
"name": "first_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"last_name": {
|
||||||
|
"name": "last_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"profiles_id_fkey": {
|
||||||
|
"name": "profiles_id_fkey",
|
||||||
|
"tableFrom": "profiles",
|
||||||
|
"tableTo": "users",
|
||||||
|
"schemaTo": "auth",
|
||||||
|
"columnsFrom": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {
|
||||||
|
"select-own-profile": {
|
||||||
|
"name": "select-own-profile",
|
||||||
|
"as": "PERMISSIVE",
|
||||||
|
"for": "SELECT",
|
||||||
|
"to": [
|
||||||
|
"authenticated"
|
||||||
|
],
|
||||||
|
"using": "\"profiles\".\"id\" = auth.uid()"
|
||||||
|
},
|
||||||
|
"update-own-profile": {
|
||||||
|
"name": "update-own-profile",
|
||||||
|
"as": "PERMISSIVE",
|
||||||
|
"for": "UPDATE",
|
||||||
|
"to": [
|
||||||
|
"authenticated"
|
||||||
|
],
|
||||||
|
"using": "\"profiles\".\"id\" = auth.uid()",
|
||||||
|
"withCheck": "\"profiles\".\"id\" = auth.uid()"
|
||||||
|
},
|
||||||
|
"insert-profile": {
|
||||||
|
"name": "insert-profile",
|
||||||
|
"as": "PERMISSIVE",
|
||||||
|
"for": "INSERT",
|
||||||
|
"to": [
|
||||||
|
"authenticated"
|
||||||
|
],
|
||||||
|
"withCheck": "\"profiles\".\"id\" = auth.uid()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": true
|
||||||
|
},
|
||||||
|
"public.demo": {
|
||||||
|
"name": "demo",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"full_name": {
|
||||||
|
"name": "full_name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"name": "phone",
|
||||||
|
"type": "varchar(256)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,6 +15,13 @@
|
|||||||
"when": 1755013739316,
|
"when": 1755013739316,
|
||||||
"tag": "0001_nice_gargoyle",
|
"tag": "0001_nice_gargoyle",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 2,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1755075449620,
|
||||||
|
"tag": "0002_bouncy_apocalypse",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
39
src/lib/hooks/user/useCreateUser.tsx
Normal file
39
src/lib/hooks/user/useCreateUser.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { useMutation } from "@tanstack/react-query"
|
||||||
|
import { toast } from "sonner"
|
||||||
|
import type z from "zod"
|
||||||
|
import { createProfile } from "@/lib/server/user"
|
||||||
|
import { profileFormSchema } from "@/lib/validation/user"
|
||||||
|
import { useValidation } from "../useValidation"
|
||||||
|
|
||||||
|
type TProfileForm = z.infer<typeof profileFormSchema>
|
||||||
|
|
||||||
|
export const useProfile = () => {
|
||||||
|
const { validate, errors } = useValidation({
|
||||||
|
defaultSchema: profileFormSchema
|
||||||
|
})
|
||||||
|
const signup = useMutation({
|
||||||
|
mutationKey: ["create-profile"],
|
||||||
|
mutationFn: async (data: TProfileForm) => createProfile({ data }),
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("Your profile is created..", {
|
||||||
|
id: "create-profile"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const validateSignup = (formData: TProfileForm) => {
|
||||||
|
const isValid = validate({ formData })
|
||||||
|
if (!isValid) {
|
||||||
|
toast.error("Don't create", {
|
||||||
|
id: "create-profile"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
signup.mutate(formData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
profile: validateSignup,
|
||||||
|
errors,
|
||||||
|
isPending: signup.isPending
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
import { redirect } from "@tanstack/react-router"
|
import { redirect } from "@tanstack/react-router"
|
||||||
import { createServerFn } from "@tanstack/react-start"
|
import { createServerFn } from "@tanstack/react-start"
|
||||||
import { db } from "@/integrations/drizzle"
|
import { db } from "@/integrations/drizzle"
|
||||||
import { users } from "@/integrations/drizzle/db/schema"
|
import { profiles, users } from "@/integrations/drizzle/db/schema"
|
||||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
||||||
import { loginFormSchema, signupFormSchema } from "../validation/user"
|
import { loginFormSchema, profileFormSchema, signupFormSchema } from "../validation/user"
|
||||||
|
|
||||||
export const getUser = createServerFn().handler(async () => {
|
export const getUser = createServerFn().handler(async () => {
|
||||||
const supabase = getSupabaseServerClient()
|
const supabase = getSupabaseServerClient()
|
||||||
@ -90,3 +90,21 @@ export const getAllUsers = createServerFn().handler(async () => {
|
|||||||
const response = await db.select().from(users)
|
const response = await db.select().from(users)
|
||||||
return response
|
return response
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const createProfile = createServerFn({ method: "POST" })
|
||||||
|
.validator(profileFormSchema)
|
||||||
|
.handler(async ({ data }) => {
|
||||||
|
await db.insert(profiles).values(data).returning();
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getProfile = createServerFn().handler(async (data) => {
|
||||||
|
const { id } = data;
|
||||||
|
|
||||||
|
const response = await db
|
||||||
|
.select()
|
||||||
|
.from(profiles)
|
||||||
|
.where(eq(profiles.id, id))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
return response[0] ?? null;
|
||||||
|
});
|
||||||
@ -10,3 +10,9 @@ export const signupFormSchema = z.object({
|
|||||||
password: z.string().min(6, "Password must be at least 6 characters long"),
|
password: z.string().min(6, "Password must be at least 6 characters long"),
|
||||||
redirectUrl: z.string().optional()
|
redirectUrl: z.string().optional()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const profileFormSchema= z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
firstName: z.string(),
|
||||||
|
lastName: z.string().optional()
|
||||||
|
})
|
||||||
@ -14,6 +14,7 @@ import { Route as LogoutRouteImport } from './routes/logout'
|
|||||||
import { Route as LoginRouteImport } from './routes/login'
|
import { Route as LoginRouteImport } from './routes/login'
|
||||||
import { Route as AuthedRouteImport } from './routes/_authed'
|
import { Route as AuthedRouteImport } from './routes/_authed'
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
|
import { Route as AuthedRegisterRouteImport } from './routes/_authed/register'
|
||||||
import { Route as AuthedPostRouteImport } from './routes/_authed/post'
|
import { Route as AuthedPostRouteImport } from './routes/_authed/post'
|
||||||
|
|
||||||
const SignupRoute = SignupRouteImport.update({
|
const SignupRoute = SignupRouteImport.update({
|
||||||
@ -40,6 +41,11 @@ const IndexRoute = IndexRouteImport.update({
|
|||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
|
const AuthedRegisterRoute = AuthedRegisterRouteImport.update({
|
||||||
|
id: '/register',
|
||||||
|
path: '/register',
|
||||||
|
getParentRoute: () => AuthedRoute,
|
||||||
|
} as any)
|
||||||
const AuthedPostRoute = AuthedPostRouteImport.update({
|
const AuthedPostRoute = AuthedPostRouteImport.update({
|
||||||
id: '/post',
|
id: '/post',
|
||||||
path: '/post',
|
path: '/post',
|
||||||
@ -52,6 +58,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/logout': typeof LogoutRoute
|
'/logout': typeof LogoutRoute
|
||||||
'/signup': typeof SignupRoute
|
'/signup': typeof SignupRoute
|
||||||
'/post': typeof AuthedPostRoute
|
'/post': typeof AuthedPostRoute
|
||||||
|
'/register': typeof AuthedRegisterRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
@ -59,6 +66,7 @@ export interface FileRoutesByTo {
|
|||||||
'/logout': typeof LogoutRoute
|
'/logout': typeof LogoutRoute
|
||||||
'/signup': typeof SignupRoute
|
'/signup': typeof SignupRoute
|
||||||
'/post': typeof AuthedPostRoute
|
'/post': typeof AuthedPostRoute
|
||||||
|
'/register': typeof AuthedRegisterRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
__root__: typeof rootRouteImport
|
__root__: typeof rootRouteImport
|
||||||
@ -68,12 +76,13 @@ export interface FileRoutesById {
|
|||||||
'/logout': typeof LogoutRoute
|
'/logout': typeof LogoutRoute
|
||||||
'/signup': typeof SignupRoute
|
'/signup': typeof SignupRoute
|
||||||
'/_authed/post': typeof AuthedPostRoute
|
'/_authed/post': typeof AuthedPostRoute
|
||||||
|
'/_authed/register': typeof AuthedRegisterRoute
|
||||||
}
|
}
|
||||||
export interface FileRouteTypes {
|
export interface FileRouteTypes {
|
||||||
fileRoutesByFullPath: FileRoutesByFullPath
|
fileRoutesByFullPath: FileRoutesByFullPath
|
||||||
fullPaths: '/' | '/login' | '/logout' | '/signup' | '/post'
|
fullPaths: '/' | '/login' | '/logout' | '/signup' | '/post' | '/register'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to: '/' | '/login' | '/logout' | '/signup' | '/post'
|
to: '/' | '/login' | '/logout' | '/signup' | '/post' | '/register'
|
||||||
id:
|
id:
|
||||||
| '__root__'
|
| '__root__'
|
||||||
| '/'
|
| '/'
|
||||||
@ -82,6 +91,7 @@ export interface FileRouteTypes {
|
|||||||
| '/logout'
|
| '/logout'
|
||||||
| '/signup'
|
| '/signup'
|
||||||
| '/_authed/post'
|
| '/_authed/post'
|
||||||
|
| '/_authed/register'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
export interface RootRouteChildren {
|
export interface RootRouteChildren {
|
||||||
@ -129,6 +139,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof IndexRouteImport
|
preLoaderRoute: typeof IndexRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
|
'/_authed/register': {
|
||||||
|
id: '/_authed/register'
|
||||||
|
path: '/register'
|
||||||
|
fullPath: '/register'
|
||||||
|
preLoaderRoute: typeof AuthedRegisterRouteImport
|
||||||
|
parentRoute: typeof AuthedRoute
|
||||||
|
}
|
||||||
'/_authed/post': {
|
'/_authed/post': {
|
||||||
id: '/_authed/post'
|
id: '/_authed/post'
|
||||||
path: '/post'
|
path: '/post'
|
||||||
@ -141,10 +158,12 @@ declare module '@tanstack/react-router' {
|
|||||||
|
|
||||||
interface AuthedRouteChildren {
|
interface AuthedRouteChildren {
|
||||||
AuthedPostRoute: typeof AuthedPostRoute
|
AuthedPostRoute: typeof AuthedPostRoute
|
||||||
|
AuthedRegisterRoute: typeof AuthedRegisterRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthedRouteChildren: AuthedRouteChildren = {
|
const AuthedRouteChildren: AuthedRouteChildren = {
|
||||||
AuthedPostRoute: AuthedPostRoute,
|
AuthedPostRoute: AuthedPostRoute,
|
||||||
|
AuthedRegisterRoute: AuthedRegisterRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthedRouteWithChildren =
|
const AuthedRouteWithChildren =
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { createFileRoute, redirect } from "@tanstack/react-router"
|
import { createFileRoute, redirect } from "@tanstack/react-router"
|
||||||
|
import { getProfile } from "@/lib/server/user"
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authed")({
|
export const Route = createFileRoute("/_authed")({
|
||||||
beforeLoad: ({ context }) => {
|
beforeLoad: ({ context }) => {
|
||||||
@ -7,6 +8,11 @@ export const Route = createFileRoute("/_authed")({
|
|||||||
// TODO: Redirect to login page
|
// TODO: Redirect to login page
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
loader: (context) => {
|
||||||
|
console.log(context);
|
||||||
|
const profile = getProfile()
|
||||||
|
console.log(profile)
|
||||||
|
},
|
||||||
errorComponent: ({ error }) => {
|
errorComponent: ({ error }) => {
|
||||||
if (error.message === "Not authenticated") {
|
if (error.message === "Not authenticated") {
|
||||||
return (
|
return (
|
||||||
|
|||||||
43
src/routes/_authed/register.tsx
Normal file
43
src/routes/_authed/register.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Button, Form, Input, Textarea } from "@heroui/react"
|
||||||
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
|
import type { FormEvent } from "react"
|
||||||
|
import { useProfile } from "@/lib/hooks/user/useCreateUser"
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_authed/register")({
|
||||||
|
component: RouteComponent
|
||||||
|
})
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
const { errors, isPending, profile } = useProfile()
|
||||||
|
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const formData = new FormData(e.currentTarget)
|
||||||
|
profile({
|
||||||
|
id: "e6472b9d-01a9-4e2e-8bdc-0ddaa9baf5d8",
|
||||||
|
firstName: formData.get("firstName") as string,
|
||||||
|
lastName: formData.get("lastName") as string
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form
|
||||||
|
className="grid gap-2 max-w-sm w-full"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
validationErrors={errors}
|
||||||
|
>
|
||||||
|
<Input name="name" label="Nombre completo" isRequired />
|
||||||
|
<Input name="location" label="Lugar" />
|
||||||
|
<Input name="company" label="Empresa" />
|
||||||
|
<Input name="role" label="Cargo" />
|
||||||
|
<Textarea name="description" label="Descripción" />
|
||||||
|
<Textarea name="differentiator" label="¿Qué te hace diferente?" />
|
||||||
|
<Input name="coverage_areas" label="Areas de cobertura" />
|
||||||
|
<Input name="services" label="Servicios" />
|
||||||
|
projects → text[] (proyectos destacados)
|
||||||
|
contact → text (número de teléfono u otro medio)
|
||||||
|
email → text
|
||||||
|
<Button isLoading={isPending} type="submit">Crear usuario</Button>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,9 +1,17 @@
|
|||||||
import { Button, Form, Input } from "@heroui/react"
|
import { Button, Form, Input } from "@heroui/react"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute, redirect } from "@tanstack/react-router"
|
||||||
import type { FormEvent } from "react"
|
import type { FormEvent } from "react"
|
||||||
import { useLogin } from "@/lib/hooks/user/useLogin"
|
import { useLogin } from "@/lib/hooks/user/useLogin"
|
||||||
|
|
||||||
export const Route = createFileRoute("/login")({
|
export const Route = createFileRoute("/login")({
|
||||||
|
beforeLoad: ({ context }) => {
|
||||||
|
if (!context?.error) {
|
||||||
|
throw redirect({
|
||||||
|
to: "/post"
|
||||||
|
})
|
||||||
|
// TODO: Redirect to login page
|
||||||
|
}
|
||||||
|
},
|
||||||
component: LoginComp
|
component: LoginComp
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user