...
This commit is contained in:
parent
cc0cb11879
commit
feaea3975e
13
drizzle/0001_old_bromley.sql
Normal file
13
drizzle/0001_old_bromley.sql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE "bookType" AS ENUM('shonen', 'seinen', 'manhua');
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE "roleEnum" AS ENUM('Member', 'Admin');
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "users" ADD COLUMN "role" "roleEnum" DEFAULT 'Member' NOT NULL;
|
2
drizzle/0002_far_xavin.sql
Normal file
2
drizzle/0002_far_xavin.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "users" ALTER COLUMN "id" SET DATA TYPE text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "users" ALTER COLUMN "id" DROP DEFAULT;
|
1
drizzle/0003_sloppy_ironclad.sql
Normal file
1
drizzle/0003_sloppy_ironclad.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "reading_progress" ALTER COLUMN "user_id" SET DATA TYPE text;
|
165
drizzle/meta/0001_snapshot.json
Normal file
165
drizzle/meta/0001_snapshot.json
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
{
|
||||||
|
"id": "94be70b1-469e-45d7-b78e-ce1cbe863401",
|
||||||
|
"prevId": "432591f9-af02-4d29-89ec-2c89c53477ff",
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "pg",
|
||||||
|
"tables": {
|
||||||
|
"books": {
|
||||||
|
"name": "books",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "bookType",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"reading_progress": {
|
||||||
|
"name": "reading_progress",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"book_id": {
|
||||||
|
"name": "book_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"chapters_read": {
|
||||||
|
"name": "chapters_read",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"date_created": {
|
||||||
|
"name": "date_created",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"reading_progress_user_id_users_id_fk": {
|
||||||
|
"name": "reading_progress_user_id_users_id_fk",
|
||||||
|
"tableFrom": "reading_progress",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"reading_progress_book_id_books_id_fk": {
|
||||||
|
"name": "reading_progress_book_id_books_id_fk",
|
||||||
|
"tableFrom": "reading_progress",
|
||||||
|
"tableTo": "books",
|
||||||
|
"columnsFrom": [
|
||||||
|
"book_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"discord_id": {
|
||||||
|
"name": "discord_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "roleEnum",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'Member'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {
|
||||||
|
"bookType": {
|
||||||
|
"name": "bookType",
|
||||||
|
"values": {
|
||||||
|
"shonen": "shonen",
|
||||||
|
"seinen": "seinen",
|
||||||
|
"manhua": "manhua"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"roleEnum": {
|
||||||
|
"name": "roleEnum",
|
||||||
|
"values": {
|
||||||
|
"Member": "Member",
|
||||||
|
"Admin": "Admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schemas": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
164
drizzle/meta/0002_snapshot.json
Normal file
164
drizzle/meta/0002_snapshot.json
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
{
|
||||||
|
"id": "704381af-461b-4b62-b176-e887328a56d6",
|
||||||
|
"prevId": "94be70b1-469e-45d7-b78e-ce1cbe863401",
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "pg",
|
||||||
|
"tables": {
|
||||||
|
"books": {
|
||||||
|
"name": "books",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "bookType",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"reading_progress": {
|
||||||
|
"name": "reading_progress",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"book_id": {
|
||||||
|
"name": "book_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"chapters_read": {
|
||||||
|
"name": "chapters_read",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"date_created": {
|
||||||
|
"name": "date_created",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"reading_progress_user_id_users_id_fk": {
|
||||||
|
"name": "reading_progress_user_id_users_id_fk",
|
||||||
|
"tableFrom": "reading_progress",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"reading_progress_book_id_books_id_fk": {
|
||||||
|
"name": "reading_progress_book_id_books_id_fk",
|
||||||
|
"tableFrom": "reading_progress",
|
||||||
|
"tableTo": "books",
|
||||||
|
"columnsFrom": [
|
||||||
|
"book_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"discord_id": {
|
||||||
|
"name": "discord_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "roleEnum",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'Member'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {
|
||||||
|
"bookType": {
|
||||||
|
"name": "bookType",
|
||||||
|
"values": {
|
||||||
|
"shonen": "shonen",
|
||||||
|
"seinen": "seinen",
|
||||||
|
"manhua": "manhua"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"roleEnum": {
|
||||||
|
"name": "roleEnum",
|
||||||
|
"values": {
|
||||||
|
"Member": "Member",
|
||||||
|
"Admin": "Admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schemas": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
164
drizzle/meta/0003_snapshot.json
Normal file
164
drizzle/meta/0003_snapshot.json
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
{
|
||||||
|
"id": "2bef9e91-c268-4187-9a46-731b75855e99",
|
||||||
|
"prevId": "704381af-461b-4b62-b176-e887328a56d6",
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "pg",
|
||||||
|
"tables": {
|
||||||
|
"books": {
|
||||||
|
"name": "books",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "bookType",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"reading_progress": {
|
||||||
|
"name": "reading_progress",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"book_id": {
|
||||||
|
"name": "book_id",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"chapters_read": {
|
||||||
|
"name": "chapters_read",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"date_created": {
|
||||||
|
"name": "date_created",
|
||||||
|
"type": "date",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"reading_progress_user_id_users_id_fk": {
|
||||||
|
"name": "reading_progress_user_id_users_id_fk",
|
||||||
|
"tableFrom": "reading_progress",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"reading_progress_book_id_books_id_fk": {
|
||||||
|
"name": "reading_progress_book_id_books_id_fk",
|
||||||
|
"tableFrom": "reading_progress",
|
||||||
|
"tableTo": "books",
|
||||||
|
"columnsFrom": [
|
||||||
|
"book_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"discord_id": {
|
||||||
|
"name": "discord_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "roleEnum",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'Member'"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {
|
||||||
|
"bookType": {
|
||||||
|
"name": "bookType",
|
||||||
|
"values": {
|
||||||
|
"shonen": "shonen",
|
||||||
|
"seinen": "seinen",
|
||||||
|
"manhua": "manhua"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"roleEnum": {
|
||||||
|
"name": "roleEnum",
|
||||||
|
"values": {
|
||||||
|
"Member": "Member",
|
||||||
|
"Admin": "Admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schemas": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,27 @@
|
|||||||
"when": 1711490394796,
|
"when": 1711490394796,
|
||||||
"tag": "0000_fixed_the_captain",
|
"tag": "0000_fixed_the_captain",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 1,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1711814267500,
|
||||||
|
"tag": "0001_old_bromley",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 2,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1711828439946,
|
||||||
|
"tag": "0002_far_xavin",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 3,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1711828480011,
|
||||||
|
"tag": "0003_sloppy_ironclad",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
9
src/app.d.ts
vendored
9
src/app.d.ts
vendored
@ -3,7 +3,14 @@
|
|||||||
declare global {
|
declare global {
|
||||||
namespace App {
|
namespace App {
|
||||||
// interface Error {}
|
// interface Error {}
|
||||||
// interface Locals {}
|
interface Locals {
|
||||||
|
user: {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
discordid: string,
|
||||||
|
role: Member | Admin
|
||||||
|
}
|
||||||
|
}
|
||||||
// interface PageData {}
|
// interface PageData {}
|
||||||
// interface PageState {}
|
// interface PageState {}
|
||||||
// interface Platform {}
|
// interface Platform {}
|
||||||
|
55
src/lib/admin/dropdown.svelte
Normal file
55
src/lib/admin/dropdown.svelte
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<script>
|
||||||
|
import { Button } from "$lib/components/ui/button";
|
||||||
|
import * as Popover from "$lib/components/ui/popover";
|
||||||
|
import * as Select from "$lib/components/ui/select";
|
||||||
|
import * as Command from "$lib/components/ui/command";
|
||||||
|
import { ChevronDown } from "lucide-svelte";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
/**
|
||||||
|
* @type {"role" | "manga"}
|
||||||
|
*/
|
||||||
|
export let type = "role";
|
||||||
|
export let placeholder = "";
|
||||||
|
|
||||||
|
const types = {
|
||||||
|
"role": ["Member", "Admin"],
|
||||||
|
"manga": ['shonen', 'seinen', 'manhua']
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = types[type];
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let selected = options[0];
|
||||||
|
|
||||||
|
/** @param {string} value */
|
||||||
|
function select(value) {
|
||||||
|
selected = value;
|
||||||
|
dispatch("select", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let name = type;
|
||||||
|
$: selected;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Select.Root
|
||||||
|
selected={{ value: selected, label: selected }}
|
||||||
|
onSelectedChange={(e) => {
|
||||||
|
select(e?.value || "");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select.Trigger class="w-[180px]">
|
||||||
|
<Select.Value placeholder={placeholder} />
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
<Select.Group>
|
||||||
|
<Select.Label>Roles</Select.Label>
|
||||||
|
{#each options as option}
|
||||||
|
<Select.Item value={option} label={option}>{option}</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Group>
|
||||||
|
</Select.Content>
|
||||||
|
<Select.Input {name} />
|
||||||
|
</Select.Root>
|
@ -1,68 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { Button } from "$lib/components/ui/button";
|
|
||||||
import * as Popover from "$lib/components/ui/popover";
|
|
||||||
import * as Select from "$lib/components/ui/select";
|
|
||||||
import * as Command from "$lib/components/ui/command";
|
|
||||||
import { ChevronDown } from "lucide-svelte";
|
|
||||||
import { createEventDispatcher } from "svelte";
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
|
|
||||||
const roles = ["Member", "Admin"];
|
|
||||||
|
|
||||||
/** @type {"Member" | "Admin"} */
|
|
||||||
export let selected = "Member";
|
|
||||||
|
|
||||||
/** @param {"Member" | "Admin"} value */
|
|
||||||
function select(value) {
|
|
||||||
console.log(value);
|
|
||||||
selected = value;
|
|
||||||
dispatch("select", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
$: selected;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- <Popover.Root>
|
|
||||||
<Popover.Trigger asChild let:builder>
|
|
||||||
<Button builders={[builder]} variant="outline" class="ml-auto">
|
|
||||||
{selected}
|
|
||||||
<ChevronDown class="ml-2 h-4 w-4 text-muted-foreground" />
|
|
||||||
</Button>
|
|
||||||
</Popover.Trigger>
|
|
||||||
<Popover.Content class="p-0" align="end">
|
|
||||||
<Command.Root>
|
|
||||||
<Command.List>
|
|
||||||
<Command.Group>
|
|
||||||
<Command.Item class="flex flex-col items-start space-y-1 px-4 py-2 cursor-pointer" on:click={() => select("Member")}>
|
|
||||||
<p>Member</p>
|
|
||||||
<p class="text-sm text-muted-foreground">
|
|
||||||
Can read manga and submit updates.
|
|
||||||
</p>
|
|
||||||
</Command.Item>
|
|
||||||
<Command.Item class="flex flex-col items-start space-y-1 px-4 py-2 cursor-pointer" on:click={() => select("Admin")}>
|
|
||||||
<p>Admin</p>
|
|
||||||
<p class="text-sm text-muted-foreground">
|
|
||||||
Can read, edit, delete and add users, manga and activity.
|
|
||||||
</p>
|
|
||||||
</Command.Item>
|
|
||||||
</Command.Group>
|
|
||||||
</Command.List>
|
|
||||||
</Command.Root>
|
|
||||||
</Popover.Content>
|
|
||||||
</Popover.Root> -->
|
|
||||||
|
|
||||||
<Select.Root>
|
|
||||||
<Select.Trigger class="w-[180px]">
|
|
||||||
<Select.Value placeholder="Select a role" />
|
|
||||||
</Select.Trigger>
|
|
||||||
<Select.Content>
|
|
||||||
<Select.Group>
|
|
||||||
<Select.Label>Roles</Select.Label>
|
|
||||||
{#each roles as role}
|
|
||||||
<Select.Item value={role} label={role}>{role}</Select.Item>
|
|
||||||
{/each}
|
|
||||||
</Select.Group>
|
|
||||||
</Select.Content>
|
|
||||||
<Select.Input name="userRole" />
|
|
||||||
</Select.Root>
|
|
8
src/lib/auth/utils.js
Normal file
8
src/lib/auth/utils.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import("@sveltejs/kit").RequestEvent} event
|
||||||
|
*/
|
||||||
|
export async function getRole(event) {
|
||||||
|
if(!event.locals.user) return -1;
|
||||||
|
return ["Member", "Admin"].indexOf(event.locals.user.role);
|
||||||
|
}
|
@ -17,14 +17,14 @@
|
|||||||
<Button variant="ghost" builders={[builder]} class="relative h-8 w-8 rounded-full">
|
<Button variant="ghost" builders={[builder]} class="relative h-8 w-8 rounded-full">
|
||||||
<Avatar.Root class="h-8 w-8">
|
<Avatar.Root class="h-8 w-8">
|
||||||
<!-- <Avatar.Image src="/avatars/01.png" alt="@{user.username}" /> -->
|
<!-- <Avatar.Image src="/avatars/01.png" alt="@{user.username}" /> -->
|
||||||
<Avatar.Fallback>{user.username?.substring(0,2)}</Avatar.Fallback>
|
<Avatar.Fallback>{user.name?.substring(0,2)}</Avatar.Fallback>
|
||||||
</Avatar.Root>
|
</Avatar.Root>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Content class="w-56" align="end">
|
<DropdownMenu.Content class="w-56" align="end">
|
||||||
<DropdownMenu.Label class="font-normal">
|
<DropdownMenu.Label class="font-normal">
|
||||||
<div class="flex flex-col space-y-1">
|
<div class="flex flex-col space-y-1">
|
||||||
<p class="text-sm font-medium leading-none">@{user.username}</p>
|
<p class="text-sm font-medium leading-none">@{user.name}</p>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu.Label>
|
</DropdownMenu.Label>
|
||||||
<DropdownMenu.Separator />
|
<DropdownMenu.Separator />
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { pgTable, serial, text, varchar } from "drizzle-orm/pg-core";
|
import { pgTable, serial, text, varchar } from "drizzle-orm/pg-core";
|
||||||
import { drizzle } from "drizzle-orm/node-postgres";
|
import { drizzle } from "drizzle-orm/node-postgres";
|
||||||
import { Client } from "pg";
|
import pg from "pg";
|
||||||
import * as env from '$env/static/private';
|
import * as env from '$env/static/private';
|
||||||
|
|
||||||
// or
|
// or
|
||||||
const client = new Client({
|
const client = new pg.Client({
|
||||||
host: env.DB_HOST,
|
host: env.DB_HOST,
|
||||||
user: env.DB_USER,
|
user: env.DB_USER,
|
||||||
password: env.DB_PASSWORD,
|
password: env.DB_PASSWORD,
|
||||||
|
@ -2,11 +2,19 @@ import { date, integer, pgEnum, pgTable, serial, text, uuid, varchar } from 'dri
|
|||||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||||
|
|
||||||
export const bookType = pgEnum('bookType', ['shonen', 'seinen', 'manhua']);
|
export const bookType = pgEnum('bookType', ['shonen', 'seinen', 'manhua']);
|
||||||
|
export const roleEnum = pgEnum('roleEnum', ['Member', 'Admin']);
|
||||||
|
|
||||||
export const usersTable = pgTable('users', {
|
export const usersTable = pgTable('users', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: text('id').primaryKey().$defaultFn(() => {
|
||||||
discord_id: text('discord_id').notNull(),
|
var text = "";
|
||||||
name: text('name').notNull()
|
var possible = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||||
|
return text;
|
||||||
|
}),
|
||||||
|
discord_id: text('discord_id').notNull(),
|
||||||
|
name: text('name').notNull(),
|
||||||
|
role: roleEnum("role").default("Member").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const booksTable = pgTable('books', {
|
export const booksTable = pgTable('books', {
|
||||||
@ -17,7 +25,7 @@ export const booksTable = pgTable('books', {
|
|||||||
|
|
||||||
export const readingUpdatesTable = pgTable('reading_progress', {
|
export const readingUpdatesTable = pgTable('reading_progress', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
user_id: uuid('user_id').references(() => usersTable.id).notNull(),
|
user_id: text('user_id').references(() => usersTable.id).notNull(),
|
||||||
book_id: uuid('book_id').references(() => booksTable.id).notNull(),
|
book_id: uuid('book_id').references(() => booksTable.id).notNull(),
|
||||||
chapters_read: integer('chapters_read').notNull(),
|
chapters_read: integer('chapters_read').notNull(),
|
||||||
date_created: date('date_created').defaultNow().notNull()
|
date_created: date('date_created').defaultNow().notNull()
|
||||||
|
@ -3,17 +3,7 @@ import { writable } from 'svelte/store'
|
|||||||
|
|
||||||
let stored = null;
|
let stored = null;
|
||||||
|
|
||||||
if(browser && localStorage.auth) {
|
|
||||||
stored = JSON.parse(localStorage.auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {import('svelte/store').Writable<{id: string, username: string} | null>}
|
* @type {import('svelte/store').Writable<{id: string, username: string} | null>}
|
||||||
*/
|
*/
|
||||||
export const userStore = writable(stored || null)
|
export const userStore = writable(stored || null)
|
||||||
|
|
||||||
userStore.subscribe((value) => {
|
|
||||||
if(browser) {
|
|
||||||
localStorage.auth = JSON.stringify(value)
|
|
||||||
}
|
|
||||||
})
|
|
@ -5,17 +5,37 @@
|
|||||||
import { Label } from "$lib/components/ui/label";
|
import { Label } from "$lib/components/ui/label";
|
||||||
import { Input } from "$lib/components/ui/input";
|
import { Input } from "$lib/components/ui/input";
|
||||||
import { userStore } from "$lib/state";
|
import { userStore } from "$lib/state";
|
||||||
|
import {onMount} from "svelte";
|
||||||
|
|
||||||
// const user = {
|
// const user = {
|
||||||
// id: "helloworld",
|
// id: "helloworld",
|
||||||
// username: "Jar Allerton",
|
// username: "Jar Allerton",
|
||||||
// };
|
// };
|
||||||
let tokenInput = "";
|
let tokenInput = "";
|
||||||
|
/** @type {string} */
|
||||||
|
let authError = "";
|
||||||
|
|
||||||
function logIn() {
|
onMount(async () => {
|
||||||
userStore.set({
|
const me = await (await fetch("/api/me")).json();
|
||||||
id: tokenInput,
|
|
||||||
username: "joe"
|
if(me.success) {
|
||||||
})
|
userStore.set(me.user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function logIn() {
|
||||||
|
let result = await fetch("/api/login", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: tokenInput
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let parsed = await result.json();
|
||||||
|
if(!parsed.success) return authError = "Invalid token";
|
||||||
|
|
||||||
|
userStore.set(parsed.user);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -35,6 +55,7 @@
|
|||||||
<Popover.Content class="w-80">
|
<Popover.Content class="w-80">
|
||||||
<div class="grid gap-4">
|
<div class="grid gap-4">
|
||||||
<div class="grid gap-2">
|
<div class="grid gap-2">
|
||||||
|
<p class="text-red-600">{authError}</p>
|
||||||
<div
|
<div
|
||||||
class="grid grid-cols-3 items-center gap-4"
|
class="grid grid-cols-3 items-center gap-4"
|
||||||
>
|
>
|
||||||
|
19
src/routes/(front)/api/login/+server.js
Normal file
19
src/routes/(front)/api/login/+server.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import db from '$lib/db/db.server';
|
||||||
|
import { usersTable } from '$lib/db/schema.server';
|
||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
|
||||||
|
/** @type {import('./$types').RequestHandler} */
|
||||||
|
export async function POST(event) {
|
||||||
|
let data = await event.request.json();
|
||||||
|
if(!data.token) return json({success: false});
|
||||||
|
|
||||||
|
let [user] = await db.select().from(usersTable).where(eq(usersTable.id, data.token));
|
||||||
|
if(!user) return json({success:false});
|
||||||
|
|
||||||
|
event.cookies.set("token", user.id, {
|
||||||
|
path: "/"
|
||||||
|
});
|
||||||
|
|
||||||
|
return json({ success: true, user });
|
||||||
|
}
|
15
src/routes/(front)/api/me/+server.js
Normal file
15
src/routes/(front)/api/me/+server.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import db from '$lib/db/db.server';
|
||||||
|
import { usersTable } from '$lib/db/schema.server';
|
||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
|
||||||
|
/** @type {import('./$types').RequestHandler} */
|
||||||
|
export async function GET(event) {
|
||||||
|
let token = event.cookies.get("token");
|
||||||
|
if(!token) return json({success: false});
|
||||||
|
|
||||||
|
let [user] = await db.select().from(usersTable).where(eq(usersTable.id, token));
|
||||||
|
if(!user) return json({success:false});
|
||||||
|
|
||||||
|
return json({ success: true, user });
|
||||||
|
}
|
27
src/routes/admin/api/users/+server.js
Normal file
27
src/routes/admin/api/users/+server.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import db from '$lib/db/db.server';
|
||||||
|
import { usersTable } from '$lib/db/schema.server';
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { getRole } from '$lib/auth/utils';
|
||||||
|
|
||||||
|
/** @type {import('./$types').RequestHandler} */
|
||||||
|
export async function GET(event) {
|
||||||
|
console.log(getRole(event))
|
||||||
|
const users = await db.select().from(usersTable);
|
||||||
|
|
||||||
|
return json({ success: true, users });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {import('./$types').RequestHandler} */
|
||||||
|
export async function PUT({ request }) {
|
||||||
|
const body = await request.json();
|
||||||
|
if (!body.role || !body.id) return new Response(null, {
|
||||||
|
status: 400
|
||||||
|
});
|
||||||
|
|
||||||
|
db.update(usersTable).set({
|
||||||
|
role: body.role
|
||||||
|
}).where(eq(usersTable.id, body.id));
|
||||||
|
|
||||||
|
return json({success: true});
|
||||||
|
}
|
61
src/routes/admin/books/+page.server.ts
Normal file
61
src/routes/admin/books/+page.server.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import db from "$lib/db/db.server";
|
||||||
|
import { booksTable, usersTable } from "$lib/db/schema.server";
|
||||||
|
import { json, type Actions, type ServerLoadEvent } from "@sveltejs/kit";
|
||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async () => {
|
||||||
|
const books = await db.select().from(booksTable);
|
||||||
|
|
||||||
|
return { books };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actions: Actions = {
|
||||||
|
create: async (event) => {
|
||||||
|
// console.log("hello");
|
||||||
|
const data = await event.request.formData();
|
||||||
|
if(!data.has("name") || !data.has("userRole")) return json({
|
||||||
|
success: false,
|
||||||
|
message: "Missing property"
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.insert(booksTable).values({
|
||||||
|
name: data.get("name")?.valueOf(),
|
||||||
|
type: data.get("type")?.valueOf()
|
||||||
|
});
|
||||||
|
|
||||||
|
return json({
|
||||||
|
success: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateType: async (event) => {
|
||||||
|
const data = await event.request.json();
|
||||||
|
|
||||||
|
if(!data.id || !data.type) return {
|
||||||
|
success: false,
|
||||||
|
message: "Missing property"
|
||||||
|
};
|
||||||
|
|
||||||
|
await db.update(booksTable).set({
|
||||||
|
type: data.type
|
||||||
|
}).where(eq(booksTable.id, data.id));
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete: async (event) => {
|
||||||
|
const data = await event.request.json();
|
||||||
|
|
||||||
|
if(!data.id) return json({
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.delete(booksTable).where(eq(data.id, booksTable.id));
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
81
src/routes/admin/books/+page.svelte
Normal file
81
src/routes/admin/books/+page.svelte
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
import Nav from "$lib/admin/nav.svelte";
|
||||||
|
import * as Avatar from "$lib/components/ui/avatar";
|
||||||
|
import Dropdown from "$lib/admin/dropdown.svelte";
|
||||||
|
import * as Dialog from "$lib/components/ui/dialog";
|
||||||
|
import { Label } from "$lib/components/ui/label";
|
||||||
|
import {Input} from "$lib/components/ui/input";
|
||||||
|
import {X} from "lucide-svelte";
|
||||||
|
import type { PageServerData } from "./$types";
|
||||||
|
|
||||||
|
|
||||||
|
export let data: PageServerData;
|
||||||
|
let books = data.books;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Nav title="Manga">
|
||||||
|
<Dialog.Root>
|
||||||
|
<Dialog.Trigger class="w-12">
|
||||||
|
<Button>
|
||||||
|
Add Manga
|
||||||
|
</Button>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
<Dialog.Content class="sm:max-w-[425px]">
|
||||||
|
<Dialog.Header>
|
||||||
|
<Dialog.Title>Create manga</Dialog.Title>
|
||||||
|
</Dialog.Header>
|
||||||
|
<form method="POST" id="form" action="create">
|
||||||
|
<div class="grid gap-4 py-4">
|
||||||
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label for="name" class="text-right">Name</Label>
|
||||||
|
<Input id="name" value="Pedro Duarte" class="col-span-3" name="name" />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label class="text-right">Type</Label>
|
||||||
|
<Dropdown type="manga" name="type" selected="shonen" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<Dialog.Footer>
|
||||||
|
<Button type="submit" form="form">Save changes</Button>
|
||||||
|
</Dialog.Footer>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
|
<div class="grid gap-6 max-w-2xl">
|
||||||
|
{#each books as book}
|
||||||
|
<div class="flex items-center justify-between space-x-4">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm font-medium leading-none">
|
||||||
|
{book.name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Dropdown selected={book.type} type="manga" on:select={val => {
|
||||||
|
console.log(val)
|
||||||
|
fetch("?/updateType", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: book.id,
|
||||||
|
type: val.detail
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}} />
|
||||||
|
<Button variant="ghost" on:click={() => {
|
||||||
|
fetch("?/delete", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: book.id
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
books = books.filter(x=>x.id!==book.id);
|
||||||
|
}}>
|
||||||
|
<X class="w-5 h-5"/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Nav>
|
@ -1,7 +1,53 @@
|
|||||||
import type { Actions } from "@sveltejs/kit";
|
import db from "$lib/db/db.server";
|
||||||
|
import { usersTable } from "$lib/db/schema.server";
|
||||||
|
import { json, type Actions, type ServerLoadEvent } from "@sveltejs/kit";
|
||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async () => {
|
||||||
|
const users = await db.select().from(usersTable);
|
||||||
|
|
||||||
|
return { users };
|
||||||
|
}
|
||||||
|
|
||||||
export const actions: Actions = {
|
export const actions: Actions = {
|
||||||
default: async (event) => {
|
create: async (event) => {
|
||||||
console.log(event.request.body);
|
// console.log("hello");
|
||||||
|
const data = await event.request.formData();
|
||||||
|
if(!data.has("name") || !data.has("userRole")) return json({
|
||||||
|
success: false,
|
||||||
|
message: "Missing property"
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.insert(usersTable).values({
|
||||||
|
name: data.get("name")?.valueOf(),
|
||||||
|
role: data.get("userRole")?.valueOf(),
|
||||||
|
discord_id: data.get("discordid")?.valueOf()
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
updateRole: async (event) => {
|
||||||
|
const data = await event.request.json();
|
||||||
|
|
||||||
|
if(!data.user_id || !data.role) return json({
|
||||||
|
success: false,
|
||||||
|
message: "Missing property"
|
||||||
|
});
|
||||||
|
|
||||||
|
db.update(usersTable).set({
|
||||||
|
role: data.role
|
||||||
|
}).where(eq(usersTable.id, data.user_id));
|
||||||
|
},
|
||||||
|
delete: async (event) => {
|
||||||
|
const data = await event.request.json();
|
||||||
|
|
||||||
|
if(!data.id) return json({
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.delete(usersTable).where(eq(data.id, usersTable.id));
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
@ -2,22 +2,17 @@
|
|||||||
import { Button } from "$lib/components/ui/button/index.js";
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
import Nav from "$lib/admin/nav.svelte";
|
import Nav from "$lib/admin/nav.svelte";
|
||||||
import * as Avatar from "$lib/components/ui/avatar";
|
import * as Avatar from "$lib/components/ui/avatar";
|
||||||
import UserRoleDropdown from "$lib/admin/user-role-dropdown.svelte";
|
import Dropdown from "$lib/admin/dropdown.svelte";
|
||||||
import * as Dialog from "$lib/components/ui/dialog";
|
import * as Dialog from "$lib/components/ui/dialog";
|
||||||
import { Label } from "$lib/components/ui/label";
|
import { Label } from "$lib/components/ui/label";
|
||||||
import {Input} from "$lib/components/ui/input";
|
import {Input} from "$lib/components/ui/input";
|
||||||
|
import {X} from "lucide-svelte";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import type { PageServerData } from "./$types";
|
||||||
|
|
||||||
const users: { username: string; role: "Member" | "Admin"; }[] = [
|
export let data: PageServerData;
|
||||||
{
|
let users = data.users;
|
||||||
username: "Joe Biden",
|
|
||||||
role: "Member",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
username: "BoeJiden",
|
|
||||||
role: "Admin",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Nav title="Users">
|
<Nav title="Users">
|
||||||
@ -31,18 +26,25 @@
|
|||||||
<Dialog.Header>
|
<Dialog.Header>
|
||||||
<Dialog.Title>Create user</Dialog.Title>
|
<Dialog.Title>Create user</Dialog.Title>
|
||||||
</Dialog.Header>
|
</Dialog.Header>
|
||||||
<div class="grid gap-4 py-4">
|
<form method="POST" id="form" action="?/create">
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid gap-4 py-4">
|
||||||
<Label for="name" class="text-right">Username</Label>
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
<Input id="name" value="Pedro Duarte" class="col-span-3" />
|
<Label for="name" class="text-right">Name</Label>
|
||||||
|
<Input id="name" value="Pedro Duarte" class="col-span-3" name="name" />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label for="discordid" class="text-right">Discord ID</Label>
|
||||||
|
<Input id="discordid" class="col-span-3" name="discordid" />
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label class="text-right">Role</Label>
|
||||||
|
<Dropdown name="role" type="role" placeholder="Select a role" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
</form>
|
||||||
<Label for="username" class="text-right">Role</Label>
|
|
||||||
<UserRoleDropdown />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Dialog.Footer>
|
<Dialog.Footer>
|
||||||
<Button type="submit">Save changes</Button>
|
<Button type="submit" form="form">Save changes</Button>
|
||||||
</Dialog.Footer>
|
</Dialog.Footer>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
</Dialog.Root>
|
</Dialog.Root>
|
||||||
@ -53,16 +55,30 @@
|
|||||||
<Avatar.Root>
|
<Avatar.Root>
|
||||||
<!-- <Avatar.Image src="/avatars/01.png" alt="Sofia Davis" /> -->
|
<!-- <Avatar.Image src="/avatars/01.png" alt="Sofia Davis" /> -->
|
||||||
<Avatar.Fallback
|
<Avatar.Fallback
|
||||||
>{user.username.substring(0, 2)}</Avatar.Fallback
|
>{user.name.substring(0, 2)}</Avatar.Fallback
|
||||||
>
|
>
|
||||||
</Avatar.Root>
|
</Avatar.Root>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-sm font-medium leading-none">
|
<p class="text-sm font-medium leading-none">
|
||||||
{user.username}
|
{user.name}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<UserRoleDropdown selected={user.role} />
|
<Dropdown selected={user.role} type="role" on:select={(e) => {
|
||||||
|
console.log(e.detail.value);
|
||||||
|
}} />
|
||||||
|
<Button variant="ghost" on:click={() => {
|
||||||
|
fetch("?/delete", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: user.id
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
users = users.filter(x=>x.id!==user.id);
|
||||||
|
}}>
|
||||||
|
<X class="w-5 h-5"/>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
11
src/routes/hooks.server.ts
Normal file
11
src/routes/hooks.server.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import db from '$lib/db/db.server';
|
||||||
|
import { usersTable } from '$lib/db/schema.server';
|
||||||
|
import type { Handle } from '@sveltejs/kit';
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
|
||||||
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
|
let session = event.cookies.get("token");
|
||||||
|
if(session) event.locals.user = await db.select().from(usersTable).where(eq(usersTable.id, session));
|
||||||
|
const response = await resolve(event);
|
||||||
|
return response;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user