mirror of
https://github.com/jorgev259/soc_site-astro.git
synced 2025-06-29 07:57:41 +00:00
Compare commits
7 commits
1cd11fea2b
...
ae22351367
| Author | SHA1 | Date | |
|---|---|---|---|
| ae22351367 | |||
| d3439321ba | |||
| 75eaf17346 | |||
| 3a9f558672 | |||
| a5fde1b6c8 | |||
| 5c4f46a41e | |||
| 042758d27c |
33 changed files with 10521 additions and 7561 deletions
8
.editorconfig
Normal file
8
.editorconfig
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
/.yarn/** linguist-vendored
|
||||||
|
/.yarn/releases/* binary
|
||||||
|
/.yarn/plugins/**/* binary
|
||||||
|
/.pnp.* binary linguist-generated
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -6,6 +6,13 @@ dist/
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/cache
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
# logs
|
# logs
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
|
|
||||||
22
.vscode/launch.json
vendored
22
.vscode/launch.json
vendored
|
|
@ -2,10 +2,28 @@
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"command": "yarn dev",
|
"command": "yarn astro dev",
|
||||||
"name": "Development server",
|
"name": "Astro",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"serverReadyAction": {
|
||||||
|
"pattern": "http://localhost:([0-9]+)/",
|
||||||
|
"uriFormat": "http://localhost:%s",
|
||||||
|
"action": "openExternally"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "yarn prisma generate --sql --watch",
|
||||||
|
"name": "Prisma",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"compounds": [
|
||||||
|
{
|
||||||
|
"name": "Deveopment server",
|
||||||
|
"configurations": ["Astro", "Prisma"],
|
||||||
|
"stopAll": true
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
nodeLinker: node-modules
|
||||||
|
|
@ -3,10 +3,10 @@ import { defineConfig, envField } from 'astro/config'
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
import node from '@astrojs/node'
|
import node from '@astrojs/node'
|
||||||
import react from '@astrojs/react'
|
import react from '@astrojs/react'
|
||||||
import paraglide from '@inlang/paraglide-astro'
|
import { paraglideVitePlugin } from '@inlang/paraglide-js'
|
||||||
import icon from 'astro-icon'
|
import icon from 'astro-icon'
|
||||||
|
|
||||||
import { languageTags } from './project.inlang/settings.json'
|
import { locales } from './project.inlang/settings.json'
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
|
@ -31,19 +31,23 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
site: 'https://sittingonclouds.net',
|
site: 'https://sittingonclouds.net',
|
||||||
i18n: {
|
i18n: {
|
||||||
locales: languageTags,
|
locales,
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
routing: {
|
routing: {
|
||||||
prefixDefaultLocale: false,
|
prefixDefaultLocale: false,
|
||||||
redirectToDefaultLocale: true
|
redirectToDefaultLocale: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
integrations: [
|
integrations: [icon({ iconDir: 'src/img/icons' }), react()],
|
||||||
paraglide({ project: './project.inlang', outdir: './src/paraglide' }),
|
vite: {
|
||||||
icon({ iconDir: 'src/img/icons' }),
|
plugins: [
|
||||||
react()
|
tailwindcss(),
|
||||||
],
|
paraglideVitePlugin({
|
||||||
vite: { plugins: [tailwindcss()] },
|
project: './project.inlang',
|
||||||
|
outdir: './src/paraglide'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
image: { domains: ['cdn.sittingonclouds.net'] },
|
image: { domains: ['cdn.sittingonclouds.net'] },
|
||||||
output: 'server',
|
output: 'server',
|
||||||
adapter: node({ mode: 'standalone' }),
|
adapter: node({ mode: 'standalone' }),
|
||||||
|
|
@ -51,7 +55,7 @@ export default defineConfig({
|
||||||
'/en/[...params]': '/[...params]',
|
'/en/[...params]': '/[...params]',
|
||||||
'/profile': { status: 307, destination: '/maintenance' },
|
'/profile': { status: 307, destination: '/maintenance' },
|
||||||
'/profile/[username]': { status: 307, destination: '/maintenance' },
|
'/profile/[username]': { status: 307, destination: '/maintenance' },
|
||||||
'/request': { status: 307, destination: '/maintenance' }
|
'/request': { status: 308, destination: '/requests' }
|
||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
checkOrigin: false
|
checkOrigin: false
|
||||||
|
|
|
||||||
|
|
@ -90,5 +90,10 @@
|
||||||
"variousGames": "Various Games",
|
"variousGames": "Various Games",
|
||||||
"searchResultsFor": "Search results for",
|
"searchResultsFor": "Search results for",
|
||||||
"firstResults": "Showing first {take} results",
|
"firstResults": "Showing first {take} results",
|
||||||
"moreResults": "Showing {start}-{end} results"
|
"moreResults": "Showing {start}-{end} results",
|
||||||
|
"requestID": "Request ID",
|
||||||
|
"userID": "userID",
|
||||||
|
"state": "State",
|
||||||
|
"createdAt": "Created At",
|
||||||
|
"updatedAt": "Updated At"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
package.json
18
package.json
|
|
@ -3,21 +3,22 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run dev",
|
"dev": "astro dev",
|
||||||
"dev": "prisma generate --sql && astro dev",
|
"build": "yarn run prisma:build && astro build",
|
||||||
"build": "prisma generate --sql && npm run paraglide:compile && astro build",
|
"prisma:migrate": "prisma migrate deploy && tsx ./prisma/migrate.ts",
|
||||||
"preview": "astro preview",
|
"prisma:build": "yarn prisma:migrate && prisma generate --sql"
|
||||||
"paraglide:compile": "paraglide-js compile --project ./project.inlang --outdir ./src\\paraglide"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ag-grid-community/locale": "^33.3.2",
|
||||||
"@astrojs/node": "9.0.0",
|
"@astrojs/node": "9.0.0",
|
||||||
"@astrojs/react": "4.1.3",
|
"@astrojs/react": "4.1.3",
|
||||||
"@astrojs/rss": "4.0.11",
|
"@astrojs/rss": "4.0.11",
|
||||||
"@inlang/paraglide-astro": "^0.2.2",
|
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.4.1",
|
||||||
"@tailwindcss/vite": "^4.0.7",
|
"@tailwindcss/vite": "^4.0.7",
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
|
"ag-grid-community": "^33.3.1",
|
||||||
|
"ag-grid-react": "^33.3.1",
|
||||||
"astro": "^5.3.0",
|
"astro": "^5.3.0",
|
||||||
"astro-icon": "^1.1.1",
|
"astro-icon": "^1.1.1",
|
||||||
"astro-seo": "^0.8.4",
|
"astro-seo": "^0.8.4",
|
||||||
|
|
@ -36,11 +37,12 @@
|
||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"superstruct": "^2.0.2",
|
"superstruct": "^2.0.2",
|
||||||
"tailwindcss": "^4.0.7",
|
"tailwindcss": "^4.0.7",
|
||||||
|
"tsx": "^4.19.4",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.6.2",
|
||||||
"use-immer": "^0.11.0"
|
"use-immer": "^0.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@inlang/paraglide-js": "1.11.2",
|
"@inlang/paraglide-js": "^2.0.12",
|
||||||
"@parcel/watcher": "^2.4.1",
|
"@parcel/watcher": "^2.4.1",
|
||||||
"@types/nodemailer": "^6.4.17",
|
"@types/nodemailer": "^6.4.17",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
|
|
@ -52,5 +54,5 @@
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-config-standard": "^7.0.0"
|
"prettier-config-standard": "^7.0.0"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
"packageManager": "yarn@4.9.1"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
prisma/data_migrations/202505091015_donator_request_limit.ts
Normal file
36
prisma/data_migrations/202505091015_donator_request_limit.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { RequestState, type Prisma } from '@prisma/client'
|
||||||
|
import prismaClient from 'utils/prisma-client'
|
||||||
|
|
||||||
|
const LIMIT_PENDING = 5
|
||||||
|
|
||||||
|
export default async function MigrationFn(tx: Prisma.TransactionClient) {
|
||||||
|
const donatorRequests = await tx.requests.findMany({
|
||||||
|
where: { donator: true, state: RequestState.PENDING }
|
||||||
|
})
|
||||||
|
const donatorMap = new Map<string, typeof donatorRequests>()
|
||||||
|
|
||||||
|
donatorRequests.forEach((request) => {
|
||||||
|
const userID = request.userID as string
|
||||||
|
donatorMap.set(userID, [...(donatorMap.get(userID) || []), request])
|
||||||
|
})
|
||||||
|
|
||||||
|
const holdRequests = new Set<number>()
|
||||||
|
|
||||||
|
for (const requests of donatorMap.values()) {
|
||||||
|
if (requests.length <= LIMIT_PENDING) continue
|
||||||
|
|
||||||
|
const end = requests.length - LIMIT_PENDING
|
||||||
|
const sortedRequests = requests.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime()).slice(0, end)
|
||||||
|
|
||||||
|
sortedRequests.forEach((r) => {
|
||||||
|
holdRequests.add(r.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await prismaClient.requests.updateMany({
|
||||||
|
where: { id: { in: Array.from(holdRequests) } },
|
||||||
|
data: { state: RequestState.HOLD }
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`Updated ${holdRequests.size} requests to HOLD state`)
|
||||||
|
}
|
||||||
37
prisma/migrate.ts
Normal file
37
prisma/migrate.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Prisma } from '@prisma/client'
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { createRequire } from 'module'
|
||||||
|
import prismaClient from 'utils/prisma-client'
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url)
|
||||||
|
|
||||||
|
export type MigrationFn = (tx: Prisma.TransactionClient) => Promise<void>
|
||||||
|
|
||||||
|
interface Migration {
|
||||||
|
id: string
|
||||||
|
migrationFn: MigrationFn
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataMigrationsPath = path.join(import.meta.dirname, 'data_migrations')
|
||||||
|
const migrationFiles = fs.readdirSync(dataMigrationsPath).filter((file) => file.endsWith('.ts'))
|
||||||
|
const migrations: Migration[] = migrationFiles
|
||||||
|
.sort()
|
||||||
|
.map((id) => ({ id, migrationFn: require(path.join(dataMigrationsPath, id)).default }))
|
||||||
|
.filter((migration) => migration.migrationFn !== undefined)
|
||||||
|
|
||||||
|
if (migrations.length === 0) console.log('No data migrations to run.')
|
||||||
|
|
||||||
|
for (const { id, migrationFn } of migrations) {
|
||||||
|
const startDate = new Date()
|
||||||
|
const migration = await prismaClient.migration.findFirst({ where: { id } })
|
||||||
|
if (migration === null) {
|
||||||
|
console.log(`Migrating ${id}`)
|
||||||
|
await prismaClient.$transaction(async (tx) => {
|
||||||
|
await migrationFn(tx)
|
||||||
|
await tx.migration.create({ data: { id } })
|
||||||
|
})
|
||||||
|
const endDate = new Date()
|
||||||
|
console.log(`Migrated ${id} in ${(endDate.getTime() - startDate.getTime()) / 1000}s`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Migration` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to alter the column `state` on the `requests` table. The data in that column could be lost. The data in that column will be cast from `VarChar(255)` to `Enum(EnumId(1))`.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `requests` MODIFY `state` ENUM('complete', 'hold', 'pending') NOT NULL;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `user` on the `requests` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `createdAt` on the `roles` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `updatedAt` on the `roles` table. All the data in the column will be lost.
|
||||||
|
- Made the column `userID` on table `requests` required. This step will fail if there are existing NULL values in that column.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
DELETE FROM `requests` WHERE `userId` IS NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `requests` DROP COLUMN `user`,
|
||||||
|
MODIFY `userID` VARCHAR(255) NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `roles` DROP COLUMN `createdAt`,
|
||||||
|
DROP COLUMN `updatedAt`;
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
# Please do not edit this file manually
|
# Please do not edit this file manually
|
||||||
# It should be added in your version-control system (e.g., Git)
|
# It should be added in your version-control system (e.g., Git)
|
||||||
provider = "mysql"
|
provider = "mysql"
|
||||||
|
|
|
||||||
|
|
@ -334,13 +334,18 @@ model related_album {
|
||||||
@@id([albumId, relatedId])
|
@@id([albumId, relatedId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum RequestState {
|
||||||
|
COMPLETE @map("complete")
|
||||||
|
HOLD @map("hold")
|
||||||
|
PENDING @map("pending")
|
||||||
|
}
|
||||||
|
|
||||||
model requests {
|
model requests {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
title String? @db.VarChar(255)
|
title String? @db.VarChar(255)
|
||||||
link String? @db.VarChar(255)
|
link String? @db.VarChar(255)
|
||||||
user String? @db.VarChar(255)
|
userID String @db.VarChar(255)
|
||||||
userID String? @db.VarChar(255)
|
state RequestState
|
||||||
state String @db.VarChar(255)
|
|
||||||
donator Boolean
|
donator Boolean
|
||||||
reason String? @db.VarChar(255)
|
reason String? @db.VarChar(255)
|
||||||
comments String? @db.VarChar(255)
|
comments String? @db.VarChar(255)
|
||||||
|
|
@ -353,8 +358,6 @@ model requests {
|
||||||
model roles {
|
model roles {
|
||||||
name String @id @db.VarChar(255)
|
name String @id @db.VarChar(255)
|
||||||
permissions Json?
|
permissions Json?
|
||||||
createdAt DateTime @db.DateTime(0)
|
|
||||||
updatedAt DateTime @db.DateTime(0)
|
|
||||||
users User_Role[]
|
users User_Role[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -402,7 +405,7 @@ model submissions {
|
||||||
model users {
|
model users {
|
||||||
id String @id @db.VarChar(255)
|
id String @id @db.VarChar(255)
|
||||||
name String @db.VarChar(20)
|
name String @db.VarChar(20)
|
||||||
username String? @unique @db.VarChar(255)
|
username String? @unique @db.VarChar(255)
|
||||||
displayUsername String @default("Default display name")
|
displayUsername String @default("Default display name")
|
||||||
email String? @unique @db.VarChar(255)
|
email String? @unique @db.VarChar(255)
|
||||||
emailVerified Boolean
|
emailVerified Boolean
|
||||||
|
|
@ -461,3 +464,8 @@ model account {
|
||||||
|
|
||||||
user users @relation(fields: [userId], references: [id])
|
user users @relation(fields: [userId], references: [id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Migration {
|
||||||
|
id String @id
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
021513eea48bfa4e8387b34adaf7bdc8f8f2c6a7b8fa2a20bc5f8a2a424f5295
|
52m6ykHaIPT1QnSo04
|
||||||
|
|
@ -1,17 +1,12 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://inlang.com/schema/project-settings",
|
"$schema": "https://inlang.com/schema/project-settings",
|
||||||
"sourceLanguageTag": "en",
|
"baseLocale": "en",
|
||||||
"languageTags": [
|
"locales": ["en"],
|
||||||
"en"
|
|
||||||
],
|
|
||||||
"modules": [
|
"modules": [
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
|
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",
|
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js",
|
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js",
|
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
|
|
||||||
],
|
],
|
||||||
"plugin.inlang.messageFormat": {
|
"plugin.inlang.messageFormat": {
|
||||||
"pathPattern": "./messages/{languageTag}.json"
|
"pathPattern": "./messages/{locale}.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
45
src/components/AgGrid/AgGridTheme.tsx
Normal file
45
src/components/AgGrid/AgGridTheme.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import {
|
||||||
|
ModuleRegistry,
|
||||||
|
ColumnAutoSizeModule,
|
||||||
|
ColumnHoverModule,
|
||||||
|
TextFilterModule,
|
||||||
|
NumberFilterModule,
|
||||||
|
DateFilterModule,
|
||||||
|
QuickFilterModule,
|
||||||
|
TextEditorModule,
|
||||||
|
LargeTextEditorModule,
|
||||||
|
SelectEditorModule,
|
||||||
|
NumberEditorModule,
|
||||||
|
DateEditorModule,
|
||||||
|
CheckboxEditorModule,
|
||||||
|
LocaleModule,
|
||||||
|
ClientSideRowModelModule,
|
||||||
|
colorSchemeDark,
|
||||||
|
themeQuartz
|
||||||
|
} from 'ag-grid-community'
|
||||||
|
|
||||||
|
ModuleRegistry.registerModules([
|
||||||
|
ColumnAutoSizeModule,
|
||||||
|
ColumnHoverModule,
|
||||||
|
TextFilterModule,
|
||||||
|
NumberFilterModule,
|
||||||
|
DateFilterModule,
|
||||||
|
QuickFilterModule,
|
||||||
|
TextEditorModule,
|
||||||
|
LargeTextEditorModule,
|
||||||
|
SelectEditorModule,
|
||||||
|
NumberEditorModule,
|
||||||
|
DateEditorModule,
|
||||||
|
CheckboxEditorModule,
|
||||||
|
LocaleModule,
|
||||||
|
ClientSideRowModelModule
|
||||||
|
])
|
||||||
|
|
||||||
|
const AgGridTheme = themeQuartz.withPart(colorSchemeDark)
|
||||||
|
export default AgGridTheme
|
||||||
|
|
||||||
|
import { AG_GRID_LOCALE_EN } from '@ag-grid-community/locale'
|
||||||
|
|
||||||
|
export const AgGridLocales = {
|
||||||
|
en: { AG_GRID_LOCALE_EN }
|
||||||
|
}
|
||||||
|
|
@ -54,7 +54,6 @@ const { session } = Astro.locals
|
||||||
<a href='/'><NavButton>{m.home()}</NavButton></a>
|
<a href='/'><NavButton>{m.home()}</NavButton></a>
|
||||||
<a href='/last-added'><NavButton>{m.lastaddednav()}</NavButton></a>
|
<a href='/last-added'><NavButton>{m.lastaddednav()}</NavButton></a>
|
||||||
<a href='/album/list'><NavButton>{m.albumlist()}</NavButton></a>
|
<a href='/album/list'><NavButton>{m.albumlist()}</NavButton></a>
|
||||||
|
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
{m.games()}
|
{m.games()}
|
||||||
<Fragment slot='items'>
|
<Fragment slot='items'>
|
||||||
|
|
@ -65,7 +64,6 @@ const { session } = Astro.locals
|
||||||
<DropdownItem href='/platform/list'>{m.platforms()}</DropdownItem>
|
<DropdownItem href='/platform/list'>{m.platforms()}</DropdownItem>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
{m.animation()}
|
{m.animation()}
|
||||||
<Fragment slot='items'>
|
<Fragment slot='items'>
|
||||||
|
|
@ -74,13 +72,13 @@ const { session } = Astro.locals
|
||||||
<DropdownItem href='/studio/list'>{m.studios()}</DropdownItem>
|
<DropdownItem href='/studio/list'>{m.studios()}</DropdownItem>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
<a href='/requests'>
|
||||||
|
<NavButton>{m.requests()}</NavButton>
|
||||||
|
</a>
|
||||||
|
|
||||||
{
|
{
|
||||||
session ? (
|
session ? (
|
||||||
<>
|
<>
|
||||||
<a href='/requests'>
|
|
||||||
<NavButton>{m.requests()}</NavButton>
|
|
||||||
</a>
|
|
||||||
<NavButton>{m.submitalbum()}</NavButton>
|
<NavButton>{m.submitalbum()}</NavButton>
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
{m.adminGrounds()}
|
{m.adminGrounds()}
|
||||||
|
|
|
||||||
36
src/components/requests/RequestsTable.tsx
Normal file
36
src/components/requests/RequestsTable.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { type ColDef, type GridOptions } from 'ag-grid-community'
|
||||||
|
import { AgGridReact } from 'ag-grid-react'
|
||||||
|
import type { Prisma } from '@prisma/client'
|
||||||
|
import { m } from 'paraglide/messages.js'
|
||||||
|
|
||||||
|
import AgGridTheme from 'components/AgGrid/AgGridTheme'
|
||||||
|
|
||||||
|
const gridOptions: GridOptions = {
|
||||||
|
ensureDomOrder: true,
|
||||||
|
enableCellTextSelection: true
|
||||||
|
}
|
||||||
|
|
||||||
|
const colDefs: ColDef[] = [
|
||||||
|
{ field: 'id', headerName: m.requestID(), filter: true },
|
||||||
|
{ field: 'title', headerName: m.requests(), filter: true },
|
||||||
|
{ field: 'userID', headerName: m.userID(), filter: true },
|
||||||
|
{ field: 'state', headerName: m.state(), filter: true },
|
||||||
|
{ field: 'createdAt', headerName: m.createdAt() },
|
||||||
|
{ field: 'updatedAt', headerName: m.updatedAt() }
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function RequestsTable(props: { initial: Prisma.requestsGetPayload<{}>[] }) {
|
||||||
|
const { initial } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-full min-h-[500px] px-4 py-4'>
|
||||||
|
<AgGridReact
|
||||||
|
gridOptions={gridOptions}
|
||||||
|
rowData={initial}
|
||||||
|
columnDefs={colDefs}
|
||||||
|
theme={AgGridTheme}
|
||||||
|
autoSizeStrategy={{ type: 'fitCellContents' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
---
|
---
|
||||||
import { Toaster } from 'react-hot-toast'
|
import { Toaster } from 'react-hot-toast'
|
||||||
|
|
||||||
import { languageTag } from '../paraglide/runtime'
|
|
||||||
import Header from 'components/Header.astro'
|
import Header from 'components/Header.astro'
|
||||||
import Footer from 'components/Footer.astro'
|
import Footer from 'components/Footer.astro'
|
||||||
|
|
||||||
import 'styles/global.css'
|
import 'styles/global.css'
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang={languageTag()} dir={Astro.locals.paraglide.dir} class='h-full'>
|
<html class='h-full'>
|
||||||
<head>
|
<head>
|
||||||
<link rel='preconnect' href='https://fonts.googleapis.com' />
|
<link rel='preconnect' href='https://fonts.googleapis.com' />
|
||||||
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin />
|
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin />
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { auth } from 'auth'
|
import { auth } from 'auth'
|
||||||
import { defineMiddleware } from 'astro:middleware'
|
import { defineMiddleware } from 'astro:middleware'
|
||||||
|
import { paraglideMiddleware } from 'paraglide/server'
|
||||||
|
|
||||||
import PAGES from 'utils/pages.json'
|
import PAGES from 'utils/pages.json'
|
||||||
import prismaClient from 'utils/prisma-client'
|
import prismaClient from 'utils/prisma-client'
|
||||||
|
|
@ -30,5 +31,5 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
||||||
context.locals.pages = []
|
context.locals.pages = []
|
||||||
}
|
}
|
||||||
|
|
||||||
return next()
|
return paraglideMiddleware(context.request, () => next())
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
---
|
---
|
||||||
export const prerender = true
|
export const prerender = true
|
||||||
|
|
||||||
|
import DefaultSEO from 'components/DefaultSEO.astro'
|
||||||
import BaseLayout from '../layouts/base.astro'
|
import BaseLayout from '../layouts/base.astro'
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<DefaultSEO />
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<div class='flex flex-1 flex-col bg-soc-green-dark py-6'>
|
<div class='flex flex-1 flex-col bg-soc-green-dark py-6'>
|
||||||
<div class='text-md` text-center'>
|
<div class='text-md` text-center'>
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ export const prerender = true
|
||||||
import { Image } from 'astro:assets'
|
import { Image } from 'astro:assets'
|
||||||
|
|
||||||
import BaseLayout from '../layouts/base.astro'
|
import BaseLayout from '../layouts/base.astro'
|
||||||
|
import DefaultSEO from 'components/DefaultSEO.astro'
|
||||||
|
|
||||||
import GuraGif from '../img/assets/doggo-thumbs-up.gif'
|
import GuraGif from '../img/assets/doggo-thumbs-up.gif'
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<DefaultSEO />
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<div class='flex flex-1 flex-col bg-soc-green-dark py-6'>
|
<div class='flex flex-1 flex-col bg-soc-green-dark py-6'>
|
||||||
<div class='text-md` text-center'>Something went wrong.... yubi yubi</div>
|
<div class='text-md` text-center'>Something went wrong.... yubi yubi</div>
|
||||||
|
|
|
||||||
27
src/pages/api/request/update.ts
Normal file
27
src/pages/api/request/update.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import type { APIRoute } from 'astro'
|
||||||
|
import * as s from 'superstruct'
|
||||||
|
import prismaClient from 'utils/prisma-client'
|
||||||
|
|
||||||
|
import { Status, parseForm } from 'utils/form'
|
||||||
|
import { EditRequest } from 'schemas/requests'
|
||||||
|
|
||||||
|
export const POST: APIRoute = async ({ request, locals }) => {
|
||||||
|
const { permissions, user } = locals
|
||||||
|
if (!user || !permissions.includes('REQUESTS')) return Status(403)
|
||||||
|
|
||||||
|
let body
|
||||||
|
try {
|
||||||
|
const formData = await parseForm(await request.formData())
|
||||||
|
body = s.create(formData, EditRequest)
|
||||||
|
} catch (err) {
|
||||||
|
return Status(422, (err as Error).message)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await prismaClient.requests.update({ where: { id: body.id }, data: { ...body, updatedAt: new Date() } })
|
||||||
|
return Status(200, body.id.toString())
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return Status(500, (err as Error).message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,15 +4,17 @@ import * as m from 'paraglide/messages'
|
||||||
import { getRandom } from 'utils/form'
|
import { getRandom } from 'utils/form'
|
||||||
import { getRandomAlbum } from 'utils/queries'
|
import { getRandomAlbum } from 'utils/queries'
|
||||||
|
|
||||||
import Base from 'layouts/base.astro'
|
import BaseLayout from 'layouts/base.astro'
|
||||||
import AlbumBox from 'components/AlbumBox.astro'
|
import AlbumBox from 'components/AlbumBox.astro'
|
||||||
|
import DefaultSEO from 'components/DefaultSEO.astro'
|
||||||
|
|
||||||
const titles = [m.holy12_0(), m.holy12_1(), m.holy12_2(), m.holy12_3(), m.holy12_4(), m.holy12_5()]
|
const titles = [m.holy12_0(), m.holy12_1(), m.holy12_2(), m.holy12_3(), m.holy12_4(), m.holy12_5()]
|
||||||
const title = getRandom(titles)
|
const title = getRandom(titles)
|
||||||
const albums: { id: number; title: string }[] = await Promise.all(Array.from({ length: 12 }, getRandomAlbum))
|
const albums: { id: number; title: string }[] = await Promise.all(Array.from({ length: 12 }, getRandomAlbum))
|
||||||
---
|
---
|
||||||
|
|
||||||
<Base>
|
<DefaultSEO />
|
||||||
|
<BaseLayout>
|
||||||
<div class='flex flex-col px-28'>
|
<div class='flex flex-col px-28'>
|
||||||
<h1 class='w-full uppercase font-medium tracking-wide text-4xl drop-shadow-2xl mt-5 mb-2 text-center'>{title}</h1>
|
<h1 class='w-full uppercase font-medium tracking-wide text-4xl drop-shadow-2xl mt-5 mb-2 text-center'>{title}</h1>
|
||||||
<div class='grid grid-cols-2 md:grid-cols-4 gap-x-1.5 mb-4'>
|
<div class='grid grid-cols-2 md:grid-cols-4 gap-x-1.5 mb-4'>
|
||||||
|
|
@ -23,4 +25,4 @@ const albums: { id: number; title: string }[] = await Promise.all(Array.from({ l
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Base>
|
</BaseLayout>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { AlbumStatus } from '@prisma/client'
|
||||||
import Sidebar from 'components/Sidebar.astro'
|
import Sidebar from 'components/Sidebar.astro'
|
||||||
import BaseLayout from 'layouts/base.astro'
|
import BaseLayout from 'layouts/base.astro'
|
||||||
import AlbumBox from 'components/AlbumBox.astro'
|
import AlbumBox from 'components/AlbumBox.astro'
|
||||||
|
import DefaultSEO from 'components/DefaultSEO.astro'
|
||||||
|
|
||||||
const recentAlbums = await prismaClient.albums.findMany({
|
const recentAlbums = await prismaClient.albums.findMany({
|
||||||
where: { status: AlbumStatus.SHOW },
|
where: { status: AlbumStatus.SHOW },
|
||||||
|
|
@ -21,6 +22,7 @@ const lastAlbums = await prismaClient.albums.findMany({
|
||||||
})
|
})
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<DefaultSEO />
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<div class='flex flex-col md:flex-row flex-1 max-w-[2000px]'>
|
<div class='flex flex-col md:flex-row flex-1 max-w-[2000px]'>
|
||||||
<div class='flex-1 px-2'>
|
<div class='flex-1 px-2'>
|
||||||
|
|
@ -50,7 +52,9 @@ const lastAlbums = await prismaClient.albums.findMany({
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class='flex gap-x-2 py-4'>
|
<div class='flex gap-x-2 py-4'>
|
||||||
<a href="/last-added" class='bg-dark uppercase rounded-md text-2xl p-1.5 font-semibold w-full'>{m.moreLastAdded}</a>
|
<a href='/last-added' class='bg-dark uppercase rounded-md text-2xl p-1.5 font-semibold w-full'
|
||||||
|
>{m.moreLastAdded}</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@ import { Image } from 'astro:assets'
|
||||||
import BaseLayout from '../layouts/base.astro'
|
import BaseLayout from '../layouts/base.astro'
|
||||||
|
|
||||||
import GuraGif from '../img/assets/doggo-thumbs-up.gif'
|
import GuraGif from '../img/assets/doggo-thumbs-up.gif'
|
||||||
|
import DefaultSEO from 'components/DefaultSEO.astro'
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<DefaultSEO />
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<div class='flex flex-1 flex-col bg-soc-green-dark py-6'>
|
<div class='flex flex-1 flex-col bg-soc-green-dark py-6'>
|
||||||
<div class='text-md` text-center'>
|
<div class='text-md` text-center'>
|
||||||
|
|
|
||||||
14
src/pages/requests.astro
Normal file
14
src/pages/requests.astro
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
import BaseLayout from 'layouts/base.astro'
|
||||||
|
import DefaultSEO from 'components/DefaultSEO.astro'
|
||||||
|
import RequestsTable from 'components/requests/RequestsTable'
|
||||||
|
|
||||||
|
import prismaClient from 'utils/prisma-client'
|
||||||
|
|
||||||
|
const initialRequests = await prismaClient.requests.findMany({ orderBy: { createdAt: 'desc' } })
|
||||||
|
---
|
||||||
|
|
||||||
|
<DefaultSEO />
|
||||||
|
<BaseLayout>
|
||||||
|
<RequestsTable client:only='react' initial={initialRequests} />
|
||||||
|
</BaseLayout>
|
||||||
|
|
@ -3,6 +3,7 @@ import * as m from 'paraglide/messages'
|
||||||
|
|
||||||
import AlbumSearch from 'components/search/AlbumSearch.astro'
|
import AlbumSearch from 'components/search/AlbumSearch.astro'
|
||||||
import BaseLayout from 'layouts/base.astro'
|
import BaseLayout from 'layouts/base.astro'
|
||||||
|
import DefaultSEO from 'components/DefaultSEO.astro'
|
||||||
|
|
||||||
const query = (Astro.url.searchParams.get('q') ?? '').trim()
|
const query = (Astro.url.searchParams.get('q') ?? '').trim()
|
||||||
const page = Astro.url.searchParams.get('page') || '1'
|
const page = Astro.url.searchParams.get('page') || '1'
|
||||||
|
|
@ -10,6 +11,7 @@ const page = Astro.url.searchParams.get('page') || '1'
|
||||||
if (!query || query.length < 1) return Astro.redirect(404)
|
if (!query || query.length < 1) return Astro.redirect(404)
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<DefaultSEO />
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<div class='w-full bg-dark'>
|
<div class='w-full bg-dark'>
|
||||||
<div class='flex max-w-[1200px] mx-auto justify-center px-8 py-3 flex-col'>
|
<div class='flex max-w-[1200px] mx-auto justify-center px-8 py-3 flex-col'>
|
||||||
|
|
|
||||||
12
src/schemas/requests.ts
Normal file
12
src/schemas/requests.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { RequestState } from '@prisma/client'
|
||||||
|
import { object, string, number, optional, enums } from 'superstruct'
|
||||||
|
|
||||||
|
export const EditRequest = object({
|
||||||
|
id: number(),
|
||||||
|
title: optional(string()),
|
||||||
|
link: optional(string()),
|
||||||
|
state: enums(Object.values(RequestState)),
|
||||||
|
reason: optional(string()),
|
||||||
|
comments: optional(string()),
|
||||||
|
message: optional(string())
|
||||||
|
})
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "react",
|
"jsxImportSource": "react",
|
||||||
"baseUrl": "src",
|
"baseUrl": "src",
|
||||||
"strictNullChecks": true
|
"strictNullChecks": true,
|
||||||
|
"allowJs": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue