diff --git a/drizzle/0001_dusty_cardiac.sql b/drizzle/0001_dusty_cardiac.sql new file mode 100644 index 0000000..543d091 --- /dev/null +++ b/drizzle/0001_dusty_cardiac.sql @@ -0,0 +1,7 @@ +ALTER TABLE "members" ADD COLUMN "cover_url" text;--> statement-breakpoint +ALTER TABLE "members" ADD COLUMN "phone" varchar(40);--> statement-breakpoint +ALTER TABLE "members" ADD COLUMN "website" varchar(200);--> statement-breakpoint +ALTER TABLE "members" ADD COLUMN "postal_code" varchar(20);--> statement-breakpoint +ALTER TABLE "members" ADD COLUMN "member_since" integer;--> statement-breakpoint +ALTER TABLE "members" ADD COLUMN "hours" text;--> statement-breakpoint +ALTER TABLE "members" ADD COLUMN "tags" text; \ No newline at end of file diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..924926f --- /dev/null +++ b/drizzle/meta/0001_snapshot.json @@ -0,0 +1,525 @@ +{ + "id": "338e83f7-c966-41fc-a2c7-14a4147353c5", + "prevId": "bf4dd99e-65a5-4e31-9d25-5fce8e585460", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.activity_log": { + "name": "activity_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "dot": { + "name": "dot", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'#2C6FB3'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "accent": { + "name": "accent", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'#E0A63C'" + }, + "tint": { + "name": "tint", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'#f6efdc'" + }, + "sort": { + "name": "sort", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "categories_slug_unique": { + "name": "categories_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.members": { + "name": "members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(120)", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "varchar(240)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "member_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "highlighted": { + "name": "highlighted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cover_url": { + "name": "cover_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phone": { + "name": "phone", + "type": "varchar(40)", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "postal_code": { + "name": "postal_code", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "member_since": { + "name": "member_since", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hours": { + "name": "hours", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "members_category_id_categories_id_fk": { + "name": "members_category_id_categories_id_fk", + "tableFrom": "members", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.membership_requests": { + "name": "membership_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "request_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'new'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.promotions": { + "name": "promotions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "varchar(120)", + "primaryKey": false, + "notNull": false + }, + "badge": { + "name": "badge", + "type": "varchar(40)", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "member_id": { + "name": "member_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "promo_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "valid_until": { + "name": "valid_until", + "type": "varchar(120)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "promotions_member_id_members_id_fk": { + "name": "promotions_member_id_members_id_fk", + "tableFrom": "promotions", + "tableTo": "members", + "columnsFrom": [ + "member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "member_id": { + "name": "member_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_member_id_members_id_fk": { + "name": "users_member_id_members_id_fk", + "tableFrom": "users", + "tableTo": "members", + "columnsFrom": [ + "member_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.member_status": { + "name": "member_status", + "schema": "public", + "values": [ + "active", + "pending" + ] + }, + "public.promo_status": { + "name": "promo_status", + "schema": "public", + "values": [ + "pending", + "live", + "expired", + "rejected" + ] + }, + "public.request_status": { + "name": "request_status", + "schema": "public", + "values": [ + "new", + "approved", + "rejected" + ] + }, + "public.role": { + "name": "role", + "schema": "public", + "values": [ + "admin", + "moderator", + "editor", + "member" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index e418d56..6017429 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1782475322634, "tag": "0000_numerous_roughhouse", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1782508812450, + "tag": "0001_dusty_cardiac", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/app/adherents/[id]/page.tsx b/src/app/adherents/[id]/page.tsx index 14903b2..cf21bcc 100644 --- a/src/app/adherents/[id]/page.tsx +++ b/src/app/adherents/[id]/page.tsx @@ -8,12 +8,25 @@ export const dynamic = "force-dynamic"; const STRIPE_WARM = "repeating-linear-gradient(45deg,#efe9da,#efe9da 12px,#e6ddc9 12px,#e6ddc9 24px)"; +const COVER_STRIPE = + "repeating-linear-gradient(45deg,#e9e1cd,#e9e1cd 16px,#e2d8c1 16px,#e2d8c1 32px)"; + +const TAG_COLORS = [ + { bg: "#f6efdc", color: "#9a6638" }, + { bg: "#e7f0f3", color: "#3f8aa3" }, + { bg: "#eef0ec", color: "#5a7a5a" }, + { bg: "#eaf0f6", color: "#2C6FB3" }, +]; function badgeColor(badge: string | null) { if (!badge) return "#1f8a5b"; return badge.includes("%") ? "#d8472b" : "#1f8a5b"; } +function initialsOf(name: string) { + return name.split(/\s+/).map((w) => w[0]).join("").slice(0, 2).toUpperCase(); +} + export async function generateMetadata({ params, }: { @@ -22,10 +35,7 @@ export async function generateMetadata({ const { id } = await params; const member = await getPublicMember(Number(id)); if (!member) return { title: "Adhérent — Plein R" }; - return { - title: `${member.name} — Plein R`, - description: member.description ?? undefined, - }; + return { title: `${member.name} — Plein R`, description: member.description ?? undefined }; } export default async function FicheAdherentPage({ @@ -42,90 +52,66 @@ export default async function FicheAdherentPage({ const promos = await getMemberLivePromotions(memberId); const accent = member.accent ?? "#E0A63C"; - const location = [member.address, member.city].filter(Boolean).join(" · "); + + const fullAddress = [member.address, [member.postalCode, member.city].filter(Boolean).join(" ")] + .filter(Boolean) + .join(" · "); + const mapQuery = encodeURIComponent( + [member.address, member.postalCode, member.city].filter(Boolean).join(", ") + ); + const paragraphs = (member.description ?? "").split("\n").map((s) => s.trim()).filter(Boolean); + const tags = (member.tags ?? "").split(",").map((s) => s.trim()).filter(Boolean); + const hours = (member.hours ?? "") + .split("\n") + .map((line) => line.split("|").map((s) => s.trim())) + .filter((p) => p[0]); + + const sectionCard: React.CSSProperties = { + background: "#fff", + border: "1px solid #e6dcc6", + borderRadius: 18, + padding: "26px 28px", + }; + const h2: React.CSSProperties = { + fontWeight: 700, + fontSize: 20, + margin: "0 0 12px", + color: "#26201a", + }; return ( -
+
{/* header */} -
+
{/* eslint-disable-next-line @next/next/no-img-element */} Plein R - - Plein R - + Plein R
-
-
- - ← Retour à l'annuaire - +
+ {/* breadcrumb */} +
+ Annuaire + {member.categoryLabel && (<>›{member.categoryLabel})} + › + {member.name}
- {/* hero card */} -
- - - - {/* vitrine photo */} + {/* cover + identity */} +
- {!member.logoUrl && ( - - photo vitrine - + {!member.coverUrl && ( + photo de couverture )} +
- - {/* info */} -
- {member.categoryLabel && ( - - {member.categoryLabel} - - )} -

- {member.name} -

- {location && ( -
- - {location} -
- )} - {member.description && ( -

- {member.description} -

- )} -
- {member.email && ( - - Contacter - - )} - {promos.length > 0 && ( - - Voir les promotions - - )} -
-
-
- - {/* promotions */} -
-

- Promotions en cours -

- - {promos.length === 0 ? ( +
- Cet adhérent n'a pas de promotion en cours pour le moment. + {!member.logoUrl && ( + {initialsOf(member.name)} + )}
- ) : ( -
- {promos.map((p) => ( -
-
- {!p.imageUrl && ( - - photo de l'offre - - )} - {p.badge && ( - - {p.badge} - - )} - {p.category && ( - - {p.category} - - )} -
-
-

- {p.title} -

-

- {p.text} -

- {p.validUntil && ( -
- {p.validUntil} -
- )} -
-
- ))} +
+
+

{member.name}

+ {member.categoryLabel && ( + {member.categoryLabel} + )} +
+
+ + {[fullAddress, member.memberSince ? `Adhérent depuis ${member.memberSince}` : null].filter(Boolean).join(" · ")} +
- )} +
+ {member.phone && ( + Appeler + )} + {mapQuery && ( + Itinéraire + )} +
+
+ + {/* two columns */} +
+ {/* LEFT */} +
+ {/* about */} +
+

À propos de l'établissement

+ {paragraphs.length > 0 ? ( + paragraphs.map((p, i) => ( +

{p}

+ )) + ) : ( +

Présentation à venir.

+ )} + {tags.length > 0 && ( +
+ {tags.map((t, i) => { + const c = TAG_COLORS[i % TAG_COLORS.length]; + return ( + {t} + ); + })} +
+ )} +
+ + {/* practical info */} +
+

Informations pratiques

+
+
+
Horaires
+ {hours.length > 0 ? ( +
+ {hours.map(([label, range], i) => { + const closed = (range ?? "").toLowerCase().includes("ferm"); + return ( +
+ {label} + {range} +
+ ); + })} +
+ ) : ( +
Horaires non communiqués.
+ )} +
+
+
Contact
+
+ {member.phone && ( +
{member.phone}
+ )} + {member.email && ( + {member.email} + )} + {member.website && ( + {member.website.replace(/^https?:\/\//, "")} + )} + {!member.phone && !member.email && !member.website && ( +
Coordonnées non communiquées.
+ )} +
+
+
+
+ + {/* map */} + {mapQuery && ( +
+
+

Nous situer

+ Ouvrir dans Google Maps → +
+