This commit is contained in:
Fergal Moran
2023-07-07 18:17:05 +01:00
parent 0741dfe0e9
commit d2b422e716
31 changed files with 116 additions and 1397 deletions

View File

@@ -3,8 +3,8 @@ import * as dotenv from "dotenv";
dotenv?.config();
export default {
schema: "./src/schema.ts",
out: "./drizzle",
schema: "./src/db/schema.ts",
out: "./src/db/migrations",
driver: "pg",
dbCredentials: {
connectionString: process.env.DATABASE_URL as string,

View File

@@ -1,99 +0,0 @@
DO $$ BEGIN
CREATE TYPE "LiveShowStatus" AS ENUM('SETUP', 'AWAITING', 'STREAMING', 'FINISHED');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "live_shows" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"slug" varchar(50) NOT NULL,
"full_name" text,
"description" varchar(256),
"status" "LiveShowStatus",
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now(),
"user_id" uuid NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "live_show_tags" (
"live_show_id" uuid NOT NULL,
"tag_id" uuid NOT NULL
);
--> statement-breakpoint
ALTER TABLE "live_show_tags" ADD CONSTRAINT "live_show_tags_live_show_id_tag_id" PRIMARY KEY("live_show_id","tag_id");
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "mixes" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"slug" varchar(50) NOT NULL,
"full_name" text,
"description" varchar(2048),
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
"user_id" uuid NOT NULL,
"is_processed" boolean DEFAULT false NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "mix_tags" (
"mix_id" uuid NOT NULL,
"tag_id" uuid NOT NULL
);
--> statement-breakpoint
ALTER TABLE "mix_tags" ADD CONSTRAINT "mix_tags_mix_id_tag_id" PRIMARY KEY("mix_id","tag_id");
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "tags" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"title" text,
"created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"username" text NOT NULL,
"email" text NOT NULL,
"emailVerified" timestamp,
"name" varchar(2048),
"profileImage" text,
"headerImage" text,
"password" varchar(1024),
"stream_key" varchar(64),
"urls" text[]
);
--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "title_idx" ON "tags" ("title");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "username_idx" ON "users" ("username");--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "live_shows" ADD CONSTRAINT "live_shows_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "live_show_tags" ADD CONSTRAINT "live_show_tags_live_show_id_mixes_id_fk" FOREIGN KEY ("live_show_id") REFERENCES "mixes"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "live_show_tags" ADD CONSTRAINT "live_show_tags_tag_id_tags_id_fk" FOREIGN KEY ("tag_id") REFERENCES "tags"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "mixes" ADD CONSTRAINT "mixes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "mix_tags" ADD CONSTRAINT "mix_tags_mix_id_mixes_id_fk" FOREIGN KEY ("mix_id") REFERENCES "mixes"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "mix_tags" ADD CONSTRAINT "mix_tags_tag_id_tags_id_fk" FOREIGN KEY ("tag_id") REFERENCES "tags"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

View File

@@ -1,408 +0,0 @@
{
"version": "5",
"dialect": "pg",
"id": "3aa3a733-36d0-4095-a8a5-f3436102791a",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"live_shows": {
"name": "live_shows",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"slug": {
"name": "slug",
"type": "varchar(50)",
"primaryKey": false,
"notNull": true
},
"full_name": {
"name": "full_name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"description": {
"name": "description",
"type": "varchar(256)",
"primaryKey": false,
"notNull": false
},
"status": {
"name": "status",
"type": "LiveShowStatus",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"live_shows_user_id_users_id_fk": {
"name": "live_shows_user_id_users_id_fk",
"tableFrom": "live_shows",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {}
},
"live_show_tags": {
"name": "live_show_tags",
"schema": "",
"columns": {
"live_show_id": {
"name": "live_show_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"tag_id": {
"name": "tag_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"live_show_tags_live_show_id_mixes_id_fk": {
"name": "live_show_tags_live_show_id_mixes_id_fk",
"tableFrom": "live_show_tags",
"tableTo": "mixes",
"columnsFrom": [
"live_show_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"live_show_tags_tag_id_tags_id_fk": {
"name": "live_show_tags_tag_id_tags_id_fk",
"tableFrom": "live_show_tags",
"tableTo": "tags",
"columnsFrom": [
"tag_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"live_show_tags_live_show_id_tag_id": {
"name": "live_show_tags_live_show_id_tag_id",
"columns": [
"live_show_id",
"tag_id"
]
}
}
},
"mixes": {
"name": "mixes",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"slug": {
"name": "slug",
"type": "varchar(50)",
"primaryKey": false,
"notNull": true
},
"full_name": {
"name": "full_name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"description": {
"name": "description",
"type": "varchar(2048)",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"user_id": {
"name": "user_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"is_processed": {
"name": "is_processed",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
}
},
"indexes": {},
"foreignKeys": {
"mixes_user_id_users_id_fk": {
"name": "mixes_user_id_users_id_fk",
"tableFrom": "mixes",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {}
},
"mix_tags": {
"name": "mix_tags",
"schema": "",
"columns": {
"mix_id": {
"name": "mix_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"tag_id": {
"name": "tag_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"mix_tags_mix_id_mixes_id_fk": {
"name": "mix_tags_mix_id_mixes_id_fk",
"tableFrom": "mix_tags",
"tableTo": "mixes",
"columnsFrom": [
"mix_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"mix_tags_tag_id_tags_id_fk": {
"name": "mix_tags_tag_id_tags_id_fk",
"tableFrom": "mix_tags",
"tableTo": "tags",
"columnsFrom": [
"tag_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"mix_tags_mix_id_tag_id": {
"name": "mix_tags_mix_id_tag_id",
"columns": [
"mix_id",
"tag_id"
]
}
}
},
"tags": {
"name": "tags",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {
"title_idx": {
"name": "title_idx",
"columns": [
"title"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {}
},
"users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"emailVerified": {
"name": "emailVerified",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "varchar(2048)",
"primaryKey": false,
"notNull": false
},
"profileImage": {
"name": "profileImage",
"type": "text",
"primaryKey": false,
"notNull": false
},
"headerImage": {
"name": "headerImage",
"type": "text",
"primaryKey": false,
"notNull": false
},
"password": {
"name": "password",
"type": "varchar(1024)",
"primaryKey": false,
"notNull": false
},
"stream_key": {
"name": "stream_key",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false
},
"urls": {
"name": "urls",
"type": "text[]",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"username_idx": {
"name": "username_idx",
"columns": [
"username"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {}
}
},
"enums": {
"LiveShowStatus": {
"name": "LiveShowStatus",
"values": {
"SETUP": "SETUP",
"AWAITING": "AWAITING",
"STREAMING": "STREAMING",
"FINISHED": "FINISHED"
}
}
},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@@ -1,13 +0,0 @@
{
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1688336894767,
"tag": "0000_mysterious_sentinels",
"breakpoints": true
}
]
}

View File

@@ -34,7 +34,7 @@
"http-status-codes": "^2.2.0",
"import-local": "^3.1.0",
"install": "^0.13.0",
"next": "13.4.8",
"next": "13.4.10-canary.0",
"next-auth": "^4.22.1",
"pg": "^8.11.1",
"postgres": "^3.3.5",

98
pnpm-lock.yaml generated
View File

@@ -19,7 +19,7 @@ dependencies:
version: 1.1.4(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)
'@sentry/nextjs':
specifier: ^7.57.0
version: 7.57.0(next@13.4.8)(react@18.2.0)
version: 7.57.0(next@13.4.10-canary.0)(react@18.2.0)
'@sindresorhus/slugify':
specifier: ^2.2.1
version: 2.2.1
@@ -34,7 +34,7 @@ dependencies:
version: 10.33.0(@trpc/server@10.33.0)
'@trpc/next':
specifier: ^10.33.0
version: 10.33.0(@tanstack/react-query@4.29.19)(@trpc/client@10.33.0)(@trpc/react-query@10.33.0)(@trpc/server@10.33.0)(next@13.4.8)(react-dom@18.2.0)(react@18.2.0)
version: 10.33.0(@tanstack/react-query@4.29.19)(@trpc/client@10.33.0)(@trpc/react-query@10.33.0)(@trpc/server@10.33.0)(next@13.4.10-canary.0)(react-dom@18.2.0)(react@18.2.0)
'@trpc/react-query':
specifier: ^10.33.0
version: 10.33.0(@tanstack/react-query@4.29.19)(@trpc/client@10.33.0)(@trpc/server@10.33.0)(react-dom@18.2.0)(react@18.2.0)
@@ -69,11 +69,11 @@ dependencies:
specifier: ^0.13.0
version: 0.13.0
next:
specifier: 13.4.8
version: 13.4.8(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
specifier: 13.4.10-canary.0
version: 13.4.10-canary.0(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
next-auth:
specifier: ^4.22.1
version: 4.22.1(next@13.4.8)(react-dom@18.2.0)(react@18.2.0)
version: 4.22.1(next@13.4.10-canary.0)(react-dom@18.2.0)(react@18.2.0)
pg:
specifier: ^8.11.1
version: 8.11.1
@@ -216,7 +216,7 @@ devDependencies:
version: 0.258.0(react@18.2.0)
next-themes:
specifier: ^0.2.1
version: 0.2.1(next@13.4.8)(react-dom@18.2.0)(react@18.2.0)
version: 0.2.1(next@13.4.10-canary.0)(react-dom@18.2.0)(react@18.2.0)
node-mocks-http:
specifier: ^1.12.2
version: 1.12.2
@@ -1788,8 +1788,12 @@ packages:
- supports-color
dev: false
/@next/env@13.4.10-canary.0:
resolution: {integrity: sha512-kOEMhom5gCLdtrFXGCrO3qUObPbXMJ0hLavKAEAx0Vme9YaDjYBqLuQUJ9UfpFzjYIXh4t3/ZuSmxnfSPxldZg==}
/@next/env@13.4.8:
resolution: {integrity: sha512-twuSf1klb3k9wXI7IZhbZGtFCWvGD4wXTY2rmvzIgVhXhs7ISThrbNyutBx3jWIL8Y/Hk9+woytFz5QsgtcRKQ==}
dev: true
/@next/eslint-plugin-next@13.4.8:
resolution: {integrity: sha512-cmfVHpxWjjcETFt2WHnoFU6EmY69QcPJRlRNAooQlNe53Ke90vg1Ci/dkPffryJZaxxiRziP9bQrV8lDVCn3Fw==}
@@ -1797,72 +1801,72 @@ packages:
glob: 7.1.7
dev: true
/@next/swc-darwin-arm64@13.4.8:
resolution: {integrity: sha512-MSFplVM4dTWOuKAUv0XR9gY7AWtMSBu9os9f+kp+s5rWhM1I2CdR3obFttd6366nS/W/VZxbPM5oEIdlIa46zA==}
/@next/swc-darwin-arm64@13.4.10-canary.0:
resolution: {integrity: sha512-7fO4zkE2S7XytxRGwCz77tPlVXdJrF0YV6hbgRi2dAkDCwSPKSqLOcwqhd945XhVXAXOyat8nvSG29lQeo8+cw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
optional: true
/@next/swc-darwin-x64@13.4.8:
resolution: {integrity: sha512-Reox+UXgonon9P0WNDE6w85DGtyBqGitl/ryznOvn6TvfxEaZIpTgeu3ZrJLU9dHSMhiK7YAM793mE/Zii2/Qw==}
/@next/swc-darwin-x64@13.4.10-canary.0:
resolution: {integrity: sha512-bm5pJejXtNuROmG1xePzmEOiFCBPAQYOBV3d0Jc4UPe8iJMHF0G/REKyaobFds5Oz0rd7O+WUqX4mx8/Mnf/eQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
requiresBuild: true
optional: true
/@next/swc-linux-arm64-gnu@13.4.8:
resolution: {integrity: sha512-kdyzYvAYtqQVgzIKNN7e1rLU8aZv86FDSRqPlOkKZlvqudvTO0iohuTPmnEEDlECeBM6qRPShNffotDcU/R2KA==}
/@next/swc-linux-arm64-gnu@13.4.10-canary.0:
resolution: {integrity: sha512-u4OtqbLYW2srSsybJrc4Dv2m+iJy9trAzUApPfRkV9KbOPKhne4oHwziKfoRIfgBdQdVacbZ/kl02hn7VWgoeQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
optional: true
/@next/swc-linux-arm64-musl@13.4.8:
resolution: {integrity: sha512-oWxx4yRkUGcR81XwbI+T0zhZ3bDF6V1aVLpG+C7hSG50ULpV8gC39UxVO22/bv93ZlcfMY4zl8xkz9Klct6dpQ==}
/@next/swc-linux-arm64-musl@13.4.10-canary.0:
resolution: {integrity: sha512-VxZ1etsSaL507WQ6xQAdaGju3ueFR623wi/1/ZxWxfGGPqyPn2LwL2Fbx8PDLv9gH6EKZdQG8lRXdUlV/LVPLw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
requiresBuild: true
optional: true
/@next/swc-linux-x64-gnu@13.4.8:
resolution: {integrity: sha512-anhtvuO6eE9YRhYnaEGTfbpH3L5gT/9qPFcNoi6xS432r/4DAtpJY8kNktqkTVevVIC/pVumqO8tV59PR3zbNg==}
/@next/swc-linux-x64-gnu@13.4.10-canary.0:
resolution: {integrity: sha512-zHSMxQtk+lH/TjY7O6vZjHPA/fE258tHA3MCbzSYMTFwFRwOS5T3bUu/9I6GLRVzVJb2EVPEtSBJKAFdwBttYA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
optional: true
/@next/swc-linux-x64-musl@13.4.8:
resolution: {integrity: sha512-aR+J4wWfNgH1DwCCBNjan7Iumx0lLtn+2/rEYuhIrYLY4vnxqSVGz9u3fXcgUwo6Q9LT8NFkaqK1vPprdq+BXg==}
/@next/swc-linux-x64-musl@13.4.10-canary.0:
resolution: {integrity: sha512-FC50Pvfy0v/FB84DfSSgESrhUUxeqCMkLpIkjMCCwOoaGiMc9RjToOjT5OZj2M1dik472MKdpKkh1+sou+7CUw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
requiresBuild: true
optional: true
/@next/swc-win32-arm64-msvc@13.4.8:
resolution: {integrity: sha512-OWBKIrJwQBTqrat0xhxEB/jcsjJR3+diD9nc/Y8F1mRdQzsn4bPsomgJyuqPVZs6Lz3K18qdIkvywmfSq75SsQ==}
/@next/swc-win32-arm64-msvc@13.4.10-canary.0:
resolution: {integrity: sha512-W/GV3UusZZczoDimCJWTxXQk62iwAAjDuVvISSLXuxuOGrRuJkHaRQQnTsN7pECRANDRIBiGDecawMuWz1iFOw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
requiresBuild: true
optional: true
/@next/swc-win32-ia32-msvc@13.4.8:
resolution: {integrity: sha512-agiPWGjUndXGTOn4ChbKipQXRA6/UPkywAWIkx7BhgGv48TiJfHTK6MGfBoL9tS6B4mtW39++uy0wFPnfD0JWg==}
/@next/swc-win32-ia32-msvc@13.4.10-canary.0:
resolution: {integrity: sha512-LBbVkr3DaC8YWndxBUk22BbXRPd2OEjP40u/lpQQZnYQBiAHRBH7GRaPUOyTR1uSAjsmwKKtGPD6GYkn1nWOtg==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
requiresBuild: true
optional: true
/@next/swc-win32-x64-msvc@13.4.8:
resolution: {integrity: sha512-UIRKoByVKbuR6SnFG4JM8EMFlJrfEGuUQ1ihxzEleWcNwRMMiVaCj1KyqfTOW8VTQhJ0u8P1Ngg6q1RwnIBTtw==}
/@next/swc-win32-x64-msvc@13.4.10-canary.0:
resolution: {integrity: sha512-jk5naKdJ0M7Bv3NRRysJu8sKDKsUqvUZYVjTyKPSYv0dKU0sdLsCO/gOcvi+Zs1i8vkS6h+yIrKxMyvM++CNGQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -2713,7 +2717,7 @@ packages:
tslib: 2.6.0
dev: false
/@sentry/nextjs@7.57.0(next@13.4.8)(react@18.2.0):
/@sentry/nextjs@7.57.0(next@13.4.10-canary.0)(react@18.2.0):
resolution: {integrity: sha512-TH7Hhs833j1k2rM5K3AqiQ7+bxrTzANZazBLEK1YVec02PpnqflVuBHSdFxT6dG7ypxOpMkN36BN5INY5HHT0Q==}
engines: {node: '>=8'}
peerDependencies:
@@ -2733,7 +2737,7 @@ packages:
'@sentry/utils': 7.57.0
'@sentry/webpack-plugin': 1.20.0
chalk: 3.0.0
next: 13.4.8(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
next: 13.4.10-canary.0(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
rollup: 2.78.0
stacktrace-parser: 0.1.10
@@ -2905,7 +2909,7 @@ packages:
'@trpc/server': 10.33.0
dev: false
/@trpc/next@10.33.0(@tanstack/react-query@4.29.19)(@trpc/client@10.33.0)(@trpc/react-query@10.33.0)(@trpc/server@10.33.0)(next@13.4.8)(react-dom@18.2.0)(react@18.2.0):
/@trpc/next@10.33.0(@tanstack/react-query@4.29.19)(@trpc/client@10.33.0)(@trpc/react-query@10.33.0)(@trpc/server@10.33.0)(next@13.4.10-canary.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-x2aRwGbXd+8Yk3WxVGF5IDWu9f/m2Na68FTU7df8WoJ4Il8T79Blr3i2CrpGrLZ25KyAJ34Mpqtaxwb1dUct0g==}
peerDependencies:
'@tanstack/react-query': ^4.18.0
@@ -2920,7 +2924,7 @@ packages:
'@trpc/client': 10.33.0(@trpc/server@10.33.0)
'@trpc/react-query': 10.33.0(@tanstack/react-query@4.29.19)(@trpc/client@10.33.0)(@trpc/server@10.33.0)(react-dom@18.2.0)(react@18.2.0)
'@trpc/server': 10.33.0
next: 13.4.8(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
next: 13.4.10-canary.0(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-ssr-prepass: 1.5.0(react@18.2.0)
@@ -3833,7 +3837,7 @@ packages:
hasBin: true
dependencies:
caniuse-lite: 1.0.30001512
electron-to-chromium: 1.4.449
electron-to-chromium: 1.4.450
node-releases: 2.0.12
update-browserslist-db: 1.0.11(browserslist@4.21.9)
@@ -4706,8 +4710,8 @@ packages:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: false
/electron-to-chromium@1.4.449:
resolution: {integrity: sha512-TxLRpRUj/107ATefeP8VIUWNOv90xJxZZbCW/eIbSZQiuiFANCx2b7u+GbVc9X4gU+xnbvypNMYVM/WArE1DNQ==}
/electron-to-chromium@1.4.450:
resolution: {integrity: sha512-BLG5HxSELlrMx7dJ2s+8SFlsCtJp37Zpk2VAxyC6CZtbc+9AJeZHfYHbrlSgdXp6saQ8StMqOTEDaBKgA7u1sw==}
/emittery@0.13.1:
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
@@ -7224,7 +7228,7 @@ packages:
engines: {node: '>= 0.6'}
dev: true
/next-auth@4.22.1(next@13.4.8)(react-dom@18.2.0)(react@18.2.0):
/next-auth@4.22.1(next@13.4.10-canary.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-NTR3f6W7/AWXKw8GSsgSyQcDW6jkslZLH8AiZa5PQ09w1kR8uHtR9rez/E9gAq/o17+p0JYHE8QjF3RoniiObA==}
peerDependencies:
next: ^12.2.5 || ^13
@@ -7239,7 +7243,7 @@ packages:
'@panva/hkdf': 1.1.1
cookie: 0.5.0
jose: 4.14.4
next: 13.4.8(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
next: 13.4.10-canary.0(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
oauth: 0.9.15
openid-client: 5.4.2
preact: 10.15.1
@@ -7249,14 +7253,14 @@ packages:
uuid: 8.3.2
dev: false
/next-themes@0.2.1(next@13.4.8)(react-dom@18.2.0)(react@18.2.0):
/next-themes@0.2.1(next@13.4.10-canary.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
peerDependencies:
next: '*'
react: '*'
react-dom: '*'
dependencies:
next: 13.4.8(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
next: 13.4.10-canary.0(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: true
@@ -7265,8 +7269,8 @@ packages:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
dev: true
/next@13.4.8(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-lxUjndYKjZHGK3CWeN2RI+/6ni6EUvjiqGWXAYPxUfGIdFGQ5XoisrqAJ/dF74aP27buAfs8MKIbIMMdxjqSBg==}
/next@13.4.10-canary.0(@babel/core@7.22.6)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-bi/HYZSeMtDjH3wNBCWpwaRJEX6K/opCCVd9shrMv79ERP+Npw6i5al7MuWCTC7yY3IAbpOG7WgTsYgnoBtnGg==}
engines: {node: '>=16.8.0'}
hasBin: true
peerDependencies:
@@ -7283,7 +7287,7 @@ packages:
sass:
optional: true
dependencies:
'@next/env': 13.4.8
'@next/env': 13.4.10-canary.0
'@opentelemetry/api': 1.4.1
'@swc/helpers': 0.5.1
busboy: 1.6.0
@@ -7295,15 +7299,15 @@ packages:
watchpack: 2.4.0
zod: 3.21.4
optionalDependencies:
'@next/swc-darwin-arm64': 13.4.8
'@next/swc-darwin-x64': 13.4.8
'@next/swc-linux-arm64-gnu': 13.4.8
'@next/swc-linux-arm64-musl': 13.4.8
'@next/swc-linux-x64-gnu': 13.4.8
'@next/swc-linux-x64-musl': 13.4.8
'@next/swc-win32-arm64-msvc': 13.4.8
'@next/swc-win32-ia32-msvc': 13.4.8
'@next/swc-win32-x64-msvc': 13.4.8
'@next/swc-darwin-arm64': 13.4.10-canary.0
'@next/swc-darwin-x64': 13.4.10-canary.0
'@next/swc-linux-arm64-gnu': 13.4.10-canary.0
'@next/swc-linux-arm64-musl': 13.4.10-canary.0
'@next/swc-linux-x64-gnu': 13.4.10-canary.0
'@next/swc-linux-x64-musl': 13.4.10-canary.0
'@next/swc-win32-arm64-msvc': 13.4.10-canary.0
'@next/swc-win32-ia32-msvc': 13.4.10-canary.0
'@next/swc-win32-x64-msvc': 13.4.10-canary.0
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 129 KiB

View File

@@ -1,19 +0,0 @@
"use client";
import Sidebar from "@/lib/components/layout/sidebar/Sidebar";
import Loading from "@/lib/components/widgets/Loading";
import { useSession } from "next-auth/react";
import React from "react";
const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
const { data: session, status } = useSession();
if (!session) return <Loading />;
return (
<div className="mx-20 -mb-16 flex h-screen overflow-hidden">
<Sidebar session={session} />
<div className="w-full">{children}</div>
</div>
);
};
export default DashboardLayout;

View File

@@ -1,150 +0,0 @@
import { Icons } from "@/components/icons";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import FollowersGraph from "@/components/widgets/stats/followers-graph";
import PlaysGraph from "@/components/widgets/stats/plays-graph";
import Image from "next/image";
import React from "react";
type DashboardPageProps = {
prop1: string;
};
const DashboardPage = ({ prop1 }: DashboardPageProps) => {
return (
<>
<div className="md:hidden">
<Image
src="/img/dashboard-light.png"
width={1280}
height={866}
alt="Dashboard"
className="block dark:hidden"
/>
<Image
src="/img/dashboard-dark.png"
width={1280}
height={866}
alt="Dashboard"
className="hidden dark:block"
/>
</div>
<div className="hidden flex-col md:flex">
<div className="flex-1 space-y-4 p-8 pt-6">
<div className="flex items-center justify-between space-y-2">
<h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
<div className="flex items-center space-x-2">
{/* <CalendarDateRangePicker /> */}
<Button size="sm">
<Icons.download className="mr-2 h-4 w-4" />
Download
</Button>
</div>
</div>
<Tabs defaultValue="overview" className="space-y-4">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="analytics" disabled>
Analytics
</TabsTrigger>
<TabsTrigger value="reports" disabled>
Reports
</TabsTrigger>
<TabsTrigger value="notifications" disabled>
Notifications
</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Total Revenue
</CardTitle>
<Icons.money className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">$45,231.89</div>
<p className="text-xs text-muted-foreground">
+20.1% from last month
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Subscriptions
</CardTitle>
<Icons.users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+2350</div>
<p className="text-xs text-muted-foreground">
+180.1% from last month
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Sales</CardTitle>
<Icons.billing className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+12,234</div>
<p className="text-xs text-muted-foreground">
+19% from last month
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Active Now
</CardTitle>
<Icons.activity className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+573</div>
<p className="text-xs text-muted-foreground">
+201 since last hour
</p>
</CardContent>
</Card>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
<Card className="col-span-4">
<CardHeader>
<CardTitle>Plays</CardTitle>
</CardHeader>
<CardContent className="pl-2">
<PlaysGraph />
</CardContent>
</Card>
<Card className="col-span-3">
<CardHeader>
<CardTitle>Recent Sales</CardTitle>
<CardDescription>
You made 265 sales this month.
</CardDescription>
</CardHeader>
<CardContent>
<FollowersGraph />
</CardContent>
</Card>
</div>
</TabsContent>
</Tabs>
</div>
</div>
</>
);
};
export default DashboardPage;

View File

@@ -1,12 +0,0 @@
"use client";
import React from "react";
import { api } from "@/lib/utils/api";
import LiveShowWrapper from "@/lib/components/show/LiveShowWrapper";
import Loading from "@/lib/components/widgets/Loading";
const LivePage = () => {
const { isFetching, data: show } = api.show.getInProgress.useQuery();
return isFetching ? <Loading /> : <LiveShowWrapper incomingShow={show} />;
};
export default LivePage;

View File

@@ -1,13 +0,0 @@
import React from "react";
const LiveLayout = ({ children }: { children: React.ReactNode }) => {
return (
// padding-top: 24px;
// padding-right: 128px;
// padding-bottom: 24px;
// padding-left: 128px;
<div className="px-28 py-6">{children}</div>
);
};
export default LiveLayout;

View File

@@ -1,8 +0,0 @@
import MixCreateComponent from "@/lib/components/mix/create/MixCreateComponent";
import React from "react";
const MixCreatePage = () => {
return <MixCreateComponent />;
};
export default MixCreatePage;

View File

@@ -1,5 +0,0 @@
const ProfileForm = () => {
return <h1>Account Form</h1>;
};
export default ProfileForm;

View File

@@ -1,22 +0,0 @@
"use client";
import Loading from "@/lib/components/widgets/Loading";
import { api } from "@/lib/utils/api";
import React from "react";
import ProfileImageEditForm from "../../components/profile-images-form";
const ProfileImagesPage = () => {
const { data: profile, status } = api.user.getProfileForSettings.useQuery();
if (status === "loading") {
return <Loading />;
}
if (!profile) {
return <h1>{"Can't find your profile"}</h1>;
}
return <ProfileImageEditForm profile={profile} />;
};
export default ProfileImagesPage;

View File

@@ -1,5 +0,0 @@
const NotificationsForm = () => {
return <h1>Notifications Form</h1>;
};
export default NotificationsForm;

View File

@@ -1,22 +0,0 @@
"use client";
import Loading from "@/lib/components/widgets/Loading";
import { api } from "@/lib/utils/api";
import { type NextPage } from "next";
import React from "react";
import ProfileEditForm from "../../components/profile-edit-form";
const ProfileSettingsPage: NextPage = () => {
const { data: profile, status } = api.user.getProfileForSettings.useQuery();
if (status === "loading") {
return <Loading />;
}
return profile ? (
<ProfileEditForm profile={profile} />
) : (
<div>Nothing here bai!</div>
);
};
export default ProfileSettingsPage;

View File

@@ -1,181 +0,0 @@
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/form";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { toast } from "@/components/ui/use-toast";
import { notice } from "@/lib/components/notifications/toast";
import { type UserModel } from "@/lib/models";
import { api } from "@/lib/utils/api";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import React from "react";
import { useFieldArray, useForm } from "react-hook-form";
import * as z from "zod";
type ProfileEditFormProps = {
profile: UserModel;
};
const formSchema = z.object({
username: z
.string()
.min(2, {
message: "Username must be at least 2 characters.",
})
.max(30, {
message: "Username must not be longer than 30 characters.",
}),
email: z.string().email(),
bio: z.string().max(160).min(0).nullable(),
urls: z
.array(
z.object({
value: z.string().url({ message: "Please enter a valid URL." }),
})
)
.optional(),
});
type FormValues = z.infer<typeof formSchema>;
const ProfileEditForm: React.FC<ProfileEditFormProps> = ({
profile,
}: ProfileEditFormProps) => {
const updateUser = api.user.updateUser.useMutation({
onSuccess: (result) => {
console.log("profile-edit-form", "onSuccess", result);
notice("Success", "Profile updated successfully");
},
});
const defaultValues: Partial<FormValues> = {
username: profile.username,
email: profile.email || "",
bio: profile.bio || "",
urls: [],
};
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues,
mode: "onChange",
});
const { fields, append } = useFieldArray({
name: "urls",
control: form.control,
});
const onSubmit = async (data: FormValues) => {
console.log("profile-edit-form", "onSubmit", data);
await updateUser.mutateAsync({
...profile,
username: data.username,
email: data.email,
bio: data.bio,
urls: [],
});
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="username" {...field} />
</FormControl>
<FormDescription>
This is your public display name. It can be your real name or a
pseudonym. You can only change this once every 30 days.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="me@gmail.com" {...field} />
</FormControl>
<FormDescription>
How we get in touch with you, we will never share or sell your
email address
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
placeholder="Tell us a little bit about yourself"
className="resize-none"
{...field}
/>
</FormControl>
<FormDescription>
You can <span>@mention</span> other users and organizations to
link to them.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div>
{fields.map((field, index) => (
<FormField
control={form.control}
key={field.id}
name={`urls.${index}.value`}
render={({ field }) => (
<FormItem>
<FormLabel className={cn(index !== 0 && "sr-only")}>
URLs
</FormLabel>
<FormDescription className={cn(index !== 0 && "sr-only")}>
Add links to your website, blog, or social media profiles.
</FormDescription>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
))}
<Button
type="button"
variant="link"
size="sm"
className="mt-1"
onClick={() => append({ value: "" })}
>
Add URL
</Button>
</div>
<Button type="submit">Update profile</Button>
</form>
</Form>
);
};
export default ProfileEditForm;

View File

@@ -1,218 +0,0 @@
"use client";
import {
Form,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/form";
import { Button } from "@/components/ui/button";
import ImageUpload from "@/components/widgets/image-upload";
import { notice } from "@/lib/components/notifications/toast";
import { type UserModel } from "@/lib/models";
import { uploadFile } from "@/lib/services/azure/upload";
import { getFileExtension } from "@/lib/utils/fileUtils";
import { api } from "@/lib/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { useSession } from "next-auth/react";
import React from "react";
import { Controller, useForm } from "react-hook-form";
import * as z from "zod";
const MAX_PROFILE_IMAGE_SIZE = 5242880;
const ACCEPTED_IMAGE_TYPES = [
"image/jpeg",
"image/jpg",
"image/png",
"image/webp",
];
type ProfileImageEditFormProps = {
profile: UserModel;
};
const ProfileImageEditForm: React.FC<ProfileImageEditFormProps> = ({
profile,
}) => {
const { update } = useSession();
const [profileImageChanged, setProfileImageChanged] = React.useState(false);
const [headerImageChanged, setHeaderImageChanged] = React.useState(false);
const uploader = api.upload.getSASToken.useQuery({ containerName: "images" });
const updateUser = api.user.updateUser.useMutation({
onSuccess: (result) => {
console.log("profile-edit-form", "onSuccess", result);
notice("Success", "Profile updated successfully");
},
});
const formSchema = z.object({
profileImage: z
.any()
.refine((file: File) => {
const ret =
!profileImageChanged || file?.size <= MAX_PROFILE_IMAGE_SIZE;
console.log("profile-images-form", "profileImage_refine", ret);
return ret;
}, `Max image size is 5MB.`)
.refine(
(file: File) =>
!profileImageChanged || ACCEPTED_IMAGE_TYPES.includes(file?.type),
"Only .jpg, .jpeg, .png and .webp formats are supported."
)
.optional(),
headerImage: z
.any()
.refine((file: File) => {
const ret =
!headerImageChanged || file?.size <= MAX_PROFILE_IMAGE_SIZE * 3;
return ret;
}, `Max image size is 15MB.`)
.refine(
(file: File) =>
!headerImageChanged || ACCEPTED_IMAGE_TYPES.includes(file?.type),
"Only .jpg, .jpeg, .png and .webp formats are supported."
)
.optional(),
});
type FormValues = z.infer<typeof formSchema>;
const defaultValues: Partial<FormValues> = {
profileImage: profile.profileImage,
headerImage: profile.headerImage,
};
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues,
mode: "onChange",
});
const onSubmit = async (data: FormValues) => {
console.log("profile-images-form", "onSubmit", data);
if (profileImageChanged || headerImageChanged) {
const token = uploader.data;
if (!token) {
notice("Error", "Unable to upload at this time");
return;
}
if (
profileImageChanged &&
data.profileImage &&
data.profileImage instanceof File
) {
const image = await uploadFile(
data.profileImage,
"images",
`profile/avatars/${profile.id}.${getFileExtension(
data.profileImage.name
)}`,
token
);
if (image) {
await updateUser.mutateAsync({
...profile,
profileImage: image,
});
}
}
if (
headerImageChanged &&
data.headerImage &&
data.headerImage instanceof File
) {
const image = await uploadFile(
data.headerImage,
"images",
`profile/headers/${profile.id}.${getFileExtension(
data.headerImage.name
)}`,
token
);
if (image) {
await updateUser.mutateAsync({
...profile,
headerImage: image,
});
}
}
await update();
}
};
return (
<Form {...form}>
{/* <p>{JSON.stringify(form.formState.errors, null, 2)}</p> */}
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="profileImage"
render={({ field }) => (
<FormItem>
<FormLabel>Profile Image</FormLabel>
<div className="w-1/2">
<Controller
control={form.control}
name={"profileImage"}
rules={{ required: "Gonna need a profile image" }}
render={({ field: { value, onChange, ...field } }) => {
return (
<ImageUpload
{...field}
imageUrl={value as string}
onImageChanged={(image) => {
setProfileImageChanged(true);
onChange(image);
}}
/>
);
}}
/>
</div>
<FormDescription>
{
"This is your avatar, it's how you will be recognised on the site"
}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="headerImage"
render={({ field }) => (
<FormItem>
<FormLabel>Header Image</FormLabel>
<Controller
control={form.control}
name={"headerImage"}
render={({ field: { value, onChange, ...field } }) => {
return (
<ImageUpload
{...field}
className="w-full"
imageUrl={value as string}
onImageChanged={(image) => {
setHeaderImageChanged(true);
onChange(image);
}}
/>
);
}}
/>
<FormDescription>
This is the image that will show at the top of your profile
page.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Update profile images</Button>
</form>
</Form>
);
};
export default ProfileImageEditForm;

View File

@@ -1,44 +0,0 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
items: {
href: string;
title: string;
}[];
}
export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
const pathname = usePathname();
return (
<nav
className={cn(
"flex space-x-2 lg:flex-col lg:space-x-0 lg:space-y-1",
className
)}
{...props}
>
{items.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
buttonVariants({ variant: "ghost" }),
pathname === item.href
? "bg-muted hover:bg-muted"
: "hover:bg-transparent hover:underline",
"justify-start"
)}
>
{item.title}
</Link>
))}
</nav>
);
}

View File

@@ -1,68 +0,0 @@
import { Separator } from "@/components/ui/separator";
import { type Metadata } from "next";
import Image from "next/image";
import { SidebarNav } from "./components/sidebar-nav";
export const metadata: Metadata = {
title: "Forms",
description: "Advanced form example using react-hook-form and Zod.",
};
const sidebarNavItems = [
{
title: "Profile",
href: "/settings/profile",
},
{
title: "Images",
href: "/settings/images",
},
{
title: "Account",
href: "/settings/account",
},
{
title: "Notifications",
href: "/settings/notifications",
},
];
interface SettingsLayoutProps {
children: React.ReactNode;
}
export default function SettingsLayout({ children }: SettingsLayoutProps) {
return (
<div className="mx-auto max-w-5xl">
<div className="md:hidden">
<Image
src="/examples/forms-light.png"
width={1280}
height={791}
alt="Forms"
className="block dark:hidden"
/>
<Image
src="/examples/forms-dark.png"
width={1280}
height={791}
alt="Forms"
className="hidden dark:block"
/>
</div>
<div className="hidden space-y-6 p-10 pb-16 md:block">
<div className="space-y-0.5">
<h2 className="text-2xl font-bold tracking-tight">Settings</h2>
<p className="text-muted-foreground">Manage your account settings.</p>
</div>
<Separator className="my-6" />
<div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
<aside className="-mx-4 lg:w-1/5">
<SidebarNav items={sidebarNavItems} />
</aside>
<div className="flex-1 lg:max-w-2xl">{children}</div>
</div>
</div>
</div>
);
}

View File

@@ -1,3 +0,0 @@
export default function SettingsProfilePage() {
return <h1>I am a page</h1>;
}

View File

@@ -1,5 +1,7 @@
import { db } from "@/server/db";
import raise from "@/lib/utils/errors";
import { mapDbAuthUserToUserModel } from "@/lib/utils/mappers/userMapper";
import { mapMixToMixModel } from "@/lib/utils/mappers/mixMapper";
async function getData(username: string) {
const user = await db.query.users.findFirst({
@@ -9,7 +11,19 @@ async function getData(username: string) {
if (!user) {
return raise("User not found");
}
return user;
const mixes = await db.query.mixes.findMany({
where: (mixes, { eq }) => eq(mixes.userId, user.id),
limit: 10,
with: {
user: {},
},
});
return {
user: mapDbAuthUserToUserModel(user),
mixes: mixes.map((m) => mapMixToMixModel(m)),
};
}
export default getData;

View File

@@ -1,19 +0,0 @@
import getData from "./data";
const UserPageLayout = async ({
children,
params,
}: {
children: React.ReactNode;
params: { username: string };
}) => {
console.log("layout", "username", params);
const user = await getData(params.username);
return (
<>
<section className="h-500-px relative block">{user.username}</section>
<div>{children}</div>
</>
);
};
export default UserPageLayout;

View File

@@ -37,7 +37,7 @@ const RootLayout = ({children}: { children: React.ReactNode }) => {
<div className="border-b bg-background/95 backdrop-blur">
<Navbar className="mx-6"/>
</div>
<div className="flex-1 space-y-4 p-8 pt-6">{children}</div>
<div className="flex-1">{children}</div>
</div>
</Providers>
</body>

View File

@@ -49,6 +49,7 @@ import {
Repeat2,
Play,
PlayCircle,
Mail,
} from "lucide-react";
export type Icon = LucideIcon;
@@ -91,6 +92,7 @@ export const Icons = {
settings: Settings,
billing: CreditCard,
ellipsis: MoreVertical,
follow: UserPlus,
add: Plus,
play: Play,
playCircle: PlayCircleIcon,
@@ -113,6 +115,7 @@ export const Icons = {
share: Share2,
sun: SunMedium,
moon: Moon,
message: Mail,
laptop: Laptop,
upload: UploadCloud,
gitHub: (props: LucideProps) => (
@@ -138,8 +141,7 @@ export const Icons = {
),
aria: (props: LucideProps) => (
<svg role="img" viewBox="0 0 24 24" fill="currentColor" {...props}>
<path
d="M13.966 22.624l-1.69-4.281H8.122l3.892-9.144 5.662 13.425zM8.884 1.376H0v21.248zm15.116 0h-8.884L24 22.624Z"/>
<path d="M13.966 22.624l-1.69-4.281H8.122l3.892-9.144 5.662 13.425zM8.884 1.376H0v21.248zm15.116 0h-8.884L24 22.624Z" />
</svg>
),
npm: (props: LucideProps) => (

View File

@@ -101,7 +101,7 @@ export const users = pgTable(
email: text("email").notNull(),
emailVerified: timestamp("emailVerified"),
name: text("name"),
bio: varchar("name", { length: 2048 }),
bio: varchar("bio", { length: 2048 }),
profileImage: text("profileImage"),
headerImage: text("headerImage"),
password: varchar("password", { length: 1024 }),

View File

@@ -44,8 +44,8 @@ const NavLink = ({ href, title, Icon }: NavLinkProps) => {
<Link
href={href}
className={cn(
"text-sm font-medium lowercase text-primary hover:opacity-60 transition-opacity",
path !== href && "text-primary"
"text-sm font-medium lowercase text-primary-foreground hover:opacity-60 transition-opacity",
path !== href && "text-primary-foreground"
)}
>
<div className="inline-flex items-center">

View File

@@ -37,7 +37,11 @@ const DashboardSidebar = ({ session }: DashboardSidebarProps) => {
<div className="h-full w-60 space-y-2 p-3 ">
<div className="flex items-center space-x-4 p-2">
{session.user.profileImage && (
<UserImage src={session.user.profileImage} status={"offline"} size={"md"} />
<UserImage
src={session.user.profileImage}
status={"offline"}
size={"md"}
/>
)}
<div>
<h2 className="text-sm font-semibold">
@@ -59,7 +63,7 @@ const DashboardSidebar = ({ session }: DashboardSidebarProps) => {
<li className="">
<Link
rel="noopener noreferrer"
href="dashboard/shows"
href={`${session.user.username}/shows`}
className="flex items-center space-x-3 rounded-md p-2"
>
<BsPersonVcard className="h-5 w-5 fill-current " />
@@ -69,7 +73,7 @@ const DashboardSidebar = ({ session }: DashboardSidebarProps) => {
<li className="">
<Link
rel="noopener noreferrer"
href="dashboard/mixes"
href={`${session.user.username}/mixes`}
className="flex items-center space-x-3 rounded-md p-2"
>
<BsPersonBoundingBox className="h-5 w-5 fill-current " />

View File

@@ -4,8 +4,8 @@ type UserModel = {
name: string | null;
email: string;
bio: string | null;
profileImage: string | null;
headerImage: string | null;
profileImage: string;
headerImage: string;
urls: string[] | null;
};
export default UserModel;

View File

@@ -12,8 +12,12 @@ const mapAuthUserToUserModel = (
name: user.name,
email: user.email,
bio: user.bio,
profileImage: user.profileImage,
headerImage: user.headerImage,
profileImage: user.profileImage
? `https://mixyboos.twic.pics/${user.profileImage}?twic=v1/resize=256`
: "/img/default-avatar.png",
headerImage: user.headerImage
? `https://mixyboos.twic.pics/${user.headerImage}?twic=v1/resize=1200x400`
: "/img/default-header.png",
urls: [],
}
: undefined;
@@ -21,7 +25,7 @@ const mapAuthUserToUserModel = (
const mapDbAuthUserToUserModel = (user: DbUser): UserModel => ({
id: user.id,
username: user.username,
name: user.name,
name: user.name || user.username,
email: user.email,
bio: user.bio,
profileImage: user.profileImage