Compare commits
4 commits
712affc83d
...
c3bf31b3d4
Author | SHA1 | Date | |
---|---|---|---|
c3bf31b3d4 | |||
55b81fac91 | |||
a657906f4f | |||
2a1cf8114e |
43 changed files with 10948 additions and 3555 deletions
|
@ -13,6 +13,11 @@ Authorization: Bearer {{$dotenv DISCORD_ACCESS_TOKEN}}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
GET https://discord.com/api/guilds/{{$dotenv DISCORD_GUILD_ID}}
|
||||||
|
Authorization: Bot {{$dotenv DISCORD_BOT_TOKEN}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
POST https://discord.com/api/oauth2/token/revoke
|
POST https://discord.com/api/oauth2/token/revoke
|
||||||
Content-Type: application/x-www-form-urlencoded
|
Content-Type: application/x-www-form-urlencoded
|
||||||
Authorization: Basic {{$dotenv DISCORD_CLIENT_ID}}:{{$dotenv DISCORD_CLIENT_SECRET}}
|
Authorization: Basic {{$dotenv DISCORD_CLIENT_ID}}:{{$dotenv DISCORD_CLIENT_SECRET}}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import "dotenv/config"
|
import "dotenv/config";
|
||||||
import type { Config } from "drizzle-kit"
|
import type { Config } from "drizzle-kit";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
schema: "./src/drizzle/schema.ts",
|
schema: "./src/drizzle/schema.ts",
|
||||||
|
@ -8,4 +8,4 @@ export default {
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
connectionString: process.env.DATABASE_URL ?? "",
|
connectionString: process.env.DATABASE_URL ?? "",
|
||||||
},
|
},
|
||||||
} satisfies Config
|
} satisfies Config;
|
||||||
|
|
24
package.json
24
package.json
|
@ -7,20 +7,30 @@
|
||||||
"start": "vinxi start",
|
"start": "vinxi start",
|
||||||
"lint": "eslint --fix \"**/*.{ts,tsx,js,jsx}\"",
|
"lint": "eslint --fix \"**/*.{ts,tsx,js,jsx}\"",
|
||||||
"push": "drizzle-kit push:pg",
|
"push": "drizzle-kit push:pg",
|
||||||
|
"discord-openapi-gen": "pnpm openapi-typescript https://raw.githubusercontent.com/discord/discord-api-spec/main/specs/openapi.json -o ./src/types/discord.d.ts",
|
||||||
|
"liljudd-openapi-gen": "pnpm openapi-typescript ./public/api/specs/liljudd.json -o ./src/types/liljudd.d.ts",
|
||||||
"typecheck": "tsc --noEmit --checkJs false --skipLibCheck"
|
"typecheck": "tsc --noEmit --checkJs false --skipLibCheck"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/core": "^0.19.0",
|
"@auth/core": "^0.19.0",
|
||||||
"@auth/drizzle-adapter": "^0.3.12",
|
"@auth/drizzle-adapter": "^0.3.12",
|
||||||
"@auth/solid-start": "0.1.2",
|
"@auth/solid-start": "0.1.2",
|
||||||
"@solidjs/meta": "^0.29.3",
|
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
||||||
"@solidjs/router": "^0.10.5",
|
"@fortawesome/pro-duotone-svg-icons": "^6.5.1",
|
||||||
"@solidjs/start": "^0.4.2",
|
"@fortawesome/pro-light-svg-icons": "^6.5.1",
|
||||||
|
"@fortawesome/pro-regular-svg-icons": "^6.5.1",
|
||||||
|
"@fortawesome/pro-solid-svg-icons": "^6.5.1",
|
||||||
|
"@fortawesome/pro-thin-svg-icons": "^6.5.1",
|
||||||
|
"@fortawesome/sharp-solid-svg-icons": "^6.5.1",
|
||||||
|
"@solidjs/meta": "^0.29.2",
|
||||||
|
"@solidjs/router": "^0.10.9",
|
||||||
|
"@solidjs/start": "^0.4.9",
|
||||||
"drizzle-orm": "^0.29.2",
|
"drizzle-orm": "^0.29.2",
|
||||||
|
"moment-timezone": "^0.5.44",
|
||||||
|
"openapi-fetch": "^0.8.2",
|
||||||
"postgres": "^3.4.3",
|
"postgres": "^3.4.3",
|
||||||
"solid-js": "^1.8.7",
|
"solid-js": "^1.8.11",
|
||||||
"solid-start": "^0.3.10",
|
"vinxi": "^0.1.2"
|
||||||
"vinxi": "0.0.62"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
||||||
|
@ -30,10 +40,12 @@
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-solid": "^0.13.1",
|
"eslint-plugin-solid": "^0.13.1",
|
||||||
|
"openapi-typescript": "^6.7.3",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"prettier-plugin-organize-imports": "^3.2.4",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"sass": "^1.69.6",
|
"sass": "^1.69.6",
|
||||||
|
"typescript": "^5.3.3",
|
||||||
"zod": "3.22.4"
|
"zod": "3.22.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
3723
pnpm-lock.yaml
3723
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
34
public/api/index.html
Normal file
34
public/api/index.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="description" content="SwaggerUI" />
|
||||||
|
<title>SwaggerUI</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="swagger-ui"></div>
|
||||||
|
<script
|
||||||
|
src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js"
|
||||||
|
crossorigin
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-standalone-preset.js"
|
||||||
|
crossorigin
|
||||||
|
></script>
|
||||||
|
<script>
|
||||||
|
window.onload = () => {
|
||||||
|
window.ui = SwaggerUIBundle({
|
||||||
|
url: "/api/specs/liljudd.json",
|
||||||
|
dom_id: "#swagger-ui",
|
||||||
|
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
|
||||||
|
layout: "StandaloneLayout",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
238
public/api/specs/liljudd.json
Normal file
238
public/api/specs/liljudd.json
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"title": "li'l Judd - OpenAPI 3.0",
|
||||||
|
"description": "None yet",
|
||||||
|
"termsOfService": "https://liljudd.ink/terms-of-service/",
|
||||||
|
"contact": {
|
||||||
|
"url": "https://liljudd.ink/contact/"
|
||||||
|
},
|
||||||
|
"version": "0.0.0"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/api/config/{guildId}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Guild configs"
|
||||||
|
],
|
||||||
|
"summary": "Find guild config by ID",
|
||||||
|
"description": "Returns a single guild config",
|
||||||
|
"operationId": "getGuildById",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "guildId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of guild config to return",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/guildConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid ID supplied"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Guild not found"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"Guild configs"
|
||||||
|
],
|
||||||
|
"summary": "Deletes a guild config by ID",
|
||||||
|
"description": "Delete a guild's config",
|
||||||
|
"operationId": "deleteGuildById",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "guildId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of guild config to delete",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "successful operation"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid ID supplied"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Guild not found"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/tp_messages/{guildId}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Time planning messages"
|
||||||
|
],
|
||||||
|
"summary": "Find guild by ID for it's tp_messages",
|
||||||
|
"description": "Returns tp_messages for a guild",
|
||||||
|
"operationId": "getTp_messagesOfGuildById",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "guildId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of guild's tp_messages to return",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/guildConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"204": {
|
||||||
|
"description": "Time planning not enabled for this guild"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid ID supplied"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Guild not found"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"guildConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"guildID": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)",
|
||||||
|
"example": "1234567890123456789"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"time_planning": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"channelID": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)",
|
||||||
|
"example": "1234567890123456789"
|
||||||
|
},
|
||||||
|
"cron": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "0 0 1 * * * 60o 1w"
|
||||||
|
},
|
||||||
|
"isAvailableRoleId": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)",
|
||||||
|
"example": "1234567890123456789"
|
||||||
|
},
|
||||||
|
"wantsToBeNotifieRoledId": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)",
|
||||||
|
"example": "1234567890123456789"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"matches": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/match"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"match": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"channelID": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)",
|
||||||
|
"example": "1234567890123456789"
|
||||||
|
},
|
||||||
|
"matchType": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(50)",
|
||||||
|
"example": "Scrim"
|
||||||
|
},
|
||||||
|
"createrId": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)",
|
||||||
|
"example": "1234567890123456789"
|
||||||
|
},
|
||||||
|
"roleId": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)",
|
||||||
|
"example": "1234567890123456789"
|
||||||
|
},
|
||||||
|
"opponentName": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(100)",
|
||||||
|
"example": "?"
|
||||||
|
},
|
||||||
|
"messsageId": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "varchar(19)",
|
||||||
|
"example": "1234567890123456789"
|
||||||
|
},
|
||||||
|
"cron": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "0 0 1 5 2 2023 60o"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {
|
||||||
|
"api_key": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"name": "api_key",
|
||||||
|
"in": "header"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
@ -1,12 +1,10 @@
|
||||||
// @refresh reload
|
// @refresh reload
|
||||||
|
import "@fortawesome/fontawesome-svg-core/styles.css";
|
||||||
import { Meta, MetaProvider, Title } from "@solidjs/meta";
|
import { Meta, MetaProvider, Title } from "@solidjs/meta";
|
||||||
import { Router } from "@solidjs/router";
|
import { Router } from "@solidjs/router";
|
||||||
import { FileRoutes } from "@solidjs/start";
|
import { FileRoutes } from "@solidjs/start";
|
||||||
import { Suspense } from "solid-js";
|
import { Suspense } from "solid-js";
|
||||||
import Footer from "./components/Footer";
|
import "./styles/global.scss";
|
||||||
import NavBar from "./components/NavBar";
|
|
||||||
import "./styles/GlobalLayout.css";
|
|
||||||
import "./styles/Layout.scss";
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -18,9 +16,8 @@ export default function App() {
|
||||||
content="The Splatoon Discord bot with unique features."
|
content="The Splatoon Discord bot with unique features."
|
||||||
/>
|
/>
|
||||||
<Title>li'l Judd - Your competitive Splatoon assistant</Title>
|
<Title>li'l Judd - Your competitive Splatoon assistant</Title>
|
||||||
<NavBar />
|
|
||||||
<Suspense>{props.children}</Suspense>
|
<Suspense>{props.children}</Suspense>
|
||||||
<Footer />
|
|
||||||
</MetaProvider>
|
</MetaProvider>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
151
src/components/FontAwesomeIcon.tsx
Normal file
151
src/components/FontAwesomeIcon.tsx
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
import {
|
||||||
|
FaSymbol,
|
||||||
|
FlipProp,
|
||||||
|
IconDefinition,
|
||||||
|
IconProp,
|
||||||
|
PullProp,
|
||||||
|
RotateProp,
|
||||||
|
SizeProp,
|
||||||
|
Transform,
|
||||||
|
} from "@fortawesome/fontawesome-svg-core";
|
||||||
|
import { type JSX } from "solid-js";
|
||||||
|
|
||||||
|
export interface FontAwesomeIconProps
|
||||||
|
extends Omit<
|
||||||
|
JSX.SvgSVGAttributes<SVGSVGElement>,
|
||||||
|
"children" | "mask" | "transform"
|
||||||
|
> {
|
||||||
|
icon: IconDefinition;
|
||||||
|
mask?: IconProp;
|
||||||
|
maskId?: string;
|
||||||
|
color?: string;
|
||||||
|
spin?: boolean;
|
||||||
|
spinPulse?: boolean;
|
||||||
|
spinReverse?: boolean;
|
||||||
|
pulse?: boolean;
|
||||||
|
beat?: boolean;
|
||||||
|
fade?: boolean;
|
||||||
|
beatFade?: boolean;
|
||||||
|
bounce?: boolean;
|
||||||
|
shake?: boolean;
|
||||||
|
flash?: boolean;
|
||||||
|
border?: boolean;
|
||||||
|
fixedWidth?: boolean;
|
||||||
|
inverse?: boolean;
|
||||||
|
listItem?: boolean;
|
||||||
|
flip?: FlipProp;
|
||||||
|
size?: SizeProp;
|
||||||
|
pull?: PullProp;
|
||||||
|
rotation?: RotateProp;
|
||||||
|
transform?: string | Transform;
|
||||||
|
symbol?: FaSymbol;
|
||||||
|
style?: JSX.CSSProperties;
|
||||||
|
tabIndex?: number;
|
||||||
|
title?: string;
|
||||||
|
titleId?: string;
|
||||||
|
swapOpacity?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idPool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
function nextUniqueId() {
|
||||||
|
let size = 12;
|
||||||
|
let id = "";
|
||||||
|
|
||||||
|
while (size-- > 0) {
|
||||||
|
id += idPool[(Math.random() * 62) | 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Path(props: { d: string | string[] }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{typeof props.d === "string" ? (
|
||||||
|
<path fill="currentColor" d={props.d} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<path class="fa-secondary" fill="currentColor" d={props.d[0]} />
|
||||||
|
<path class="fa-primary" fill="currentColor" d={props.d[1]} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FontAwesomeIcon(props: FontAwesomeIconProps) {
|
||||||
|
const titleId = () =>
|
||||||
|
props.title
|
||||||
|
? "svg-inline--fa-title-".concat(props.titleId || nextUniqueId())
|
||||||
|
: undefined;
|
||||||
|
// Get CSS class list from the props object
|
||||||
|
function attributes() {
|
||||||
|
const defaultClasses = {
|
||||||
|
"svg-inline--fa": true,
|
||||||
|
[`fa-${props.icon.iconName}`]: true,
|
||||||
|
[props.class ?? ""]:
|
||||||
|
typeof props.class !== "undefined" && props.class !== null,
|
||||||
|
...props.classList,
|
||||||
|
};
|
||||||
|
|
||||||
|
// map of CSS class names to properties
|
||||||
|
const faClasses = {
|
||||||
|
"fa-beat": props.beat,
|
||||||
|
"fa-fade": props.fade,
|
||||||
|
"fa-beat-fade": props.beatFade,
|
||||||
|
"fa-bounce": props.bounce,
|
||||||
|
"fa-shake": props.shake,
|
||||||
|
"fa-flash": props.flash,
|
||||||
|
"fa-spin": props.spin,
|
||||||
|
"fa-spin-reverse": props.spinReverse,
|
||||||
|
"fa-spin-pulse": props.spinPulse,
|
||||||
|
"fa-pulse": props.pulse,
|
||||||
|
"fa-fw": props.fixedWidth,
|
||||||
|
"fa-inverse": props.inverse,
|
||||||
|
"fa-border": props.border,
|
||||||
|
"fa-li": props.listItem,
|
||||||
|
"fa-flip": typeof props.flip !== "undefined" && props.flip !== null,
|
||||||
|
"fa-flip-horizontal":
|
||||||
|
props.flip === "horizontal" || props.flip === "both",
|
||||||
|
"fa-flip-vertical": props.flip === "vertical" || props.flip === "both",
|
||||||
|
[`fa-${props.size}`]:
|
||||||
|
typeof props.size !== "undefined" && props.size !== null,
|
||||||
|
[`fa-rotate-${props.rotation}`]:
|
||||||
|
typeof props.rotation !== "undefined" && props.size !== null,
|
||||||
|
[`fa-pull-${props.pull}`]:
|
||||||
|
typeof props.pull !== "undefined" && props.pull !== null,
|
||||||
|
"fa-swap-opacity": props.swapOpacity,
|
||||||
|
};
|
||||||
|
|
||||||
|
const attributes = {
|
||||||
|
focusable: !!props.title,
|
||||||
|
"aria-hidden": !props.title,
|
||||||
|
role: "img",
|
||||||
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
|
"aria-labelledby": titleId(),
|
||||||
|
"data-prefix": props.icon.prefix,
|
||||||
|
"data-icon": props.icon.iconName,
|
||||||
|
"data-fa-transform": props.transform,
|
||||||
|
"data-fa-mask": props.mask,
|
||||||
|
"data-fa-mask-id": props.maskId,
|
||||||
|
"data-fa-symbol": props.symbol,
|
||||||
|
tabIndex: props.tabIndex,
|
||||||
|
classList: { ...defaultClasses, ...faClasses },
|
||||||
|
color: props.color,
|
||||||
|
style: props.style,
|
||||||
|
viewBox: `0 0 ${props.icon.icon[0]} ${props.icon.icon[1]}`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// return the complete class list
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg {...attributes()}>
|
||||||
|
{/* <Show when={props.title}>
|
||||||
|
<title id={titleId()}>{props.title}</title>
|
||||||
|
</Show> */}
|
||||||
|
<Path d={props.icon.icon[4]} />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
18
src/components/Layout.tsx
Normal file
18
src/components/Layout.tsx
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { JSX, Suspense } from "solid-js";
|
||||||
|
import "../styles/Layout.scss";
|
||||||
|
import Footer from "./Footer";
|
||||||
|
import NavBar from "./NavBar";
|
||||||
|
|
||||||
|
function Layout(props: { children: JSX.Element; site: string }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NavBar />
|
||||||
|
<div class={props.site}>
|
||||||
|
<Suspense>{props.children}</Suspense>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Layout;
|
|
@ -1,35 +1,53 @@
|
||||||
|
import { faCirclePlus } from "@fortawesome/pro-regular-svg-icons";
|
||||||
|
import { JSX, Show, Suspense } from "solid-js";
|
||||||
import "../styles/components/NavBar.scss";
|
import "../styles/components/NavBar.scss";
|
||||||
|
import { FontAwesomeIcon } from "./FontAwesomeIcon";
|
||||||
|
import NavUser from "./NavUser";
|
||||||
|
|
||||||
|
export function Li(props: {
|
||||||
|
href: string;
|
||||||
|
action?: () => void;
|
||||||
|
name?: string;
|
||||||
|
children?: JSX.Element;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<li class="navElem flex-row thick">
|
||||||
|
<a
|
||||||
|
class="flex-row"
|
||||||
|
href={props.href}
|
||||||
|
onClick={() => props.action && props.action()}
|
||||||
|
>
|
||||||
|
{props.children ?? <></>}
|
||||||
|
<Show when={props.name}>
|
||||||
|
<span>{props.name}</span>
|
||||||
|
</Show>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function NavBar() {
|
function NavBar() {
|
||||||
return (
|
return (
|
||||||
<nav>
|
<nav class="flex-row responsive">
|
||||||
<ul>
|
<ul class="flex-row responsive thick">
|
||||||
<li class="navElem">
|
<Li href="/" name="li'l Judd">
|
||||||
<a class="textBx" href="/">
|
<img src="/assets/logox256.png" alt="The Bots Logo" />
|
||||||
<img id="logo" src="/assets/logox256.png" alt="The Bots Logo" />
|
</Li>
|
||||||
li'l Judd
|
<Li href="/features" name="Features" />
|
||||||
</a>
|
<Li href="/how-do-i" name="How do I...?" />
|
||||||
</li>
|
<Li href="/stack" name="The Stack" />
|
||||||
<li class="navElem">
|
<Li href="/about" name="About" />
|
||||||
<a href="/features">Features</a>
|
</ul>
|
||||||
</li>
|
<ul class="flex-row responsive thick">
|
||||||
<li class="navElem">
|
<Li
|
||||||
<a href="/how-do-i">How do I...?</a>
|
|
||||||
</li>
|
|
||||||
<li class="navElem">
|
|
||||||
<a href="/stack">The Stack</a>
|
|
||||||
</li>
|
|
||||||
<li class="navElem">
|
|
||||||
<a href="/about">About</a>
|
|
||||||
</li>
|
|
||||||
<li class="navElem">
|
|
||||||
<a
|
|
||||||
href="https://discord.com/api/oauth2/authorize?client_id=1024410658973941862&permissions=18977581952080&scope=bot"
|
href="https://discord.com/api/oauth2/authorize?client_id=1024410658973941862&permissions=18977581952080&scope=bot"
|
||||||
target="_blank"
|
name="Invite to your server"
|
||||||
>
|
>
|
||||||
Invite to your server
|
<FontAwesomeIcon class="lower" icon={faCirclePlus} size="xl" />
|
||||||
</a>
|
</Li>
|
||||||
</li>
|
<Suspense>
|
||||||
|
<NavUser />
|
||||||
|
</Suspense>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
|
|
87
src/components/NavUser.tsx
Normal file
87
src/components/NavUser.tsx
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { getSession } from "@auth/solid-start";
|
||||||
|
import { signIn, signOut } from "@auth/solid-start/client";
|
||||||
|
import {
|
||||||
|
faArrowRightFromBracket,
|
||||||
|
faArrowRightToBracket,
|
||||||
|
faGear,
|
||||||
|
} from "@fortawesome/pro-regular-svg-icons";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { Show, createResource } from "solid-js";
|
||||||
|
import { getRequestEvent } from "solid-js/web";
|
||||||
|
import db from "~/drizzle";
|
||||||
|
import { users } from "~/drizzle/schema";
|
||||||
|
import { authOptions } from "~/server/auth";
|
||||||
|
import { FontAwesomeIcon } from "./FontAwesomeIcon";
|
||||||
|
import { Li } from "./NavBar";
|
||||||
|
|
||||||
|
const initialUser = {
|
||||||
|
id: "",
|
||||||
|
name: null as string | null,
|
||||||
|
email: "",
|
||||||
|
emailVerified: null as Date | null,
|
||||||
|
image: null as string | null,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getUser() {
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
const event = getRequestEvent();
|
||||||
|
if (!event)
|
||||||
|
return { success: false, message: "No request event!", ...initialUser };
|
||||||
|
|
||||||
|
const session = await getSession(event.request, authOptions);
|
||||||
|
if (!session?.user?.id)
|
||||||
|
return { success: false, message: "No user with id!", ...initialUser };
|
||||||
|
|
||||||
|
const user = (
|
||||||
|
await db
|
||||||
|
.selectDistinct()
|
||||||
|
.from(users)
|
||||||
|
.where(eq(users.id, session.user?.id))
|
||||||
|
.limit(1)
|
||||||
|
.execute()
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
return { success: true, message: "", ...user };
|
||||||
|
}
|
||||||
|
|
||||||
|
function NavUser() {
|
||||||
|
const [user] = createResource(() =>
|
||||||
|
getUser().then((e) => {
|
||||||
|
if (!e.success) console.log(1, e.message);
|
||||||
|
console.log(2, e);
|
||||||
|
return e;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Show
|
||||||
|
when={user()?.id}
|
||||||
|
fallback={
|
||||||
|
<Li
|
||||||
|
href="#"
|
||||||
|
name="Login"
|
||||||
|
action={() => signIn("discord", { callbackUrl: "/config" })}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
class="secondary"
|
||||||
|
icon={faArrowRightToBracket}
|
||||||
|
size="xl"
|
||||||
|
/>
|
||||||
|
</Li>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Li href="/config">
|
||||||
|
<div class="swap lower">
|
||||||
|
<img class="primary" src={user()?.image ?? ""} alt="User pfp" />
|
||||||
|
<FontAwesomeIcon class="secondary" icon={faGear} size="xl" />
|
||||||
|
</div>
|
||||||
|
</Li>
|
||||||
|
<Li href="#" action={() => signOut({ callbackUrl: "/" })}>
|
||||||
|
<FontAwesomeIcon icon={faArrowRightFromBracket} size="xl" />
|
||||||
|
</Li>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NavUser;
|
|
@ -1,10 +1,10 @@
|
||||||
import { drizzle } from "drizzle-orm/postgres-js"
|
import { drizzle } from "drizzle-orm/postgres-js";
|
||||||
import postgres from "postgres"
|
import postgres from "postgres";
|
||||||
import * as schema from "./schema"
|
import * as schema from "./schema";
|
||||||
|
|
||||||
const queryClient = postgres(process.env.DATABASE_URL ?? "")
|
const queryClient = postgres(import.meta.env.VITE_DATABASE_URL ?? "");
|
||||||
const db = drizzle(queryClient, {
|
const db = drizzle(queryClient, {
|
||||||
schema,
|
schema,
|
||||||
})
|
});
|
||||||
|
|
||||||
export default db
|
export default db;
|
||||||
|
|
|
@ -12,23 +12,22 @@ import {
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
export const users = pgTable("user", {
|
export const users = pgTable("user", {
|
||||||
id: text("id").primaryKey(),
|
id: text("id").notNull().primaryKey(),
|
||||||
name: text("name"),
|
name: text("name"),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
emailVerified: timestamp("email_verified", { mode: "date" }),
|
emailVerified: timestamp("emailVerified", { mode: "date" }),
|
||||||
image: text("image"),
|
image: text("image"),
|
||||||
createdAt: timestamp("created_at").defaultNow(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const accounts = pgTable(
|
export const accounts = pgTable(
|
||||||
"account",
|
"account",
|
||||||
{
|
{
|
||||||
userId: text("user_id")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
||||||
provider: text("provider").notNull(),
|
provider: text("provider").notNull(),
|
||||||
providerAccountId: text("provider_account_id").notNull(),
|
providerAccountId: text("providerAccountId").notNull(),
|
||||||
refresh_token: text("refresh_token"),
|
refresh_token: text("refresh_token"),
|
||||||
access_token: text("access_token"),
|
access_token: text("access_token"),
|
||||||
expires_at: integer("expires_at"),
|
expires_at: integer("expires_at"),
|
||||||
|
@ -45,15 +44,15 @@ export const accounts = pgTable(
|
||||||
);
|
);
|
||||||
|
|
||||||
export const sessions = pgTable("session", {
|
export const sessions = pgTable("session", {
|
||||||
sessionToken: text("session_token").primaryKey(),
|
sessionToken: text("sessionToken").notNull().primaryKey(),
|
||||||
userId: text("user_id")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const verificationTokens = pgTable(
|
export const verificationTokens = pgTable(
|
||||||
"verification_token",
|
"verificationToken",
|
||||||
{
|
{
|
||||||
identifier: text("identifier").notNull(),
|
identifier: text("identifier").notNull(),
|
||||||
token: text("token").notNull(),
|
token: text("token").notNull(),
|
||||||
|
@ -66,14 +65,14 @@ export const verificationTokens = pgTable(
|
||||||
|
|
||||||
export const matchPlannings = pgTable("match_planning", {
|
export const matchPlannings = pgTable("match_planning", {
|
||||||
id: serial("id").primaryKey(),
|
id: serial("id").primaryKey(),
|
||||||
channelId: varchar("channel_id", { length: 19 }).notNull(),
|
channelId: varchar("channel_id", { length: 20 }).notNull(),
|
||||||
matchtype: varchar("matchtype", { length: 50 }).notNull(),
|
matchtype: varchar("match_type", { length: 50 }).notNull(),
|
||||||
createrId: varchar("creater_id", { length: 19 }).notNull(),
|
createrId: varchar("creater_id", { length: 20 }).notNull(),
|
||||||
roleId: varchar("role_id", { length: 19 }).notNull(),
|
roleId: varchar("role_id", { length: 20 }).notNull(),
|
||||||
opponentName: varchar("opponent_name", { length: 100 }).notNull(),
|
opponentName: varchar("opponent_name", { length: 100 }).notNull(),
|
||||||
messageId: varchar("message_id", { length: 19 }).notNull(),
|
messageId: varchar("message_id", { length: 20 }).notNull(),
|
||||||
plannedFor: timestamp("planned_for", { precision: 3 }).notNull(),
|
ts: timestamp("ts").notNull(),
|
||||||
guildId: varchar("guild_id", { length: 19 })
|
guildId: varchar("guild_id", { length: 20 })
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => guilds.id, { onDelete: "cascade" }),
|
.references(() => guilds.id, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
@ -86,7 +85,8 @@ export const matchPlanningsRelations = relations(matchPlannings, ({ one }) => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const guilds = pgTable("guild", {
|
export const guilds = pgTable("guild", {
|
||||||
id: varchar("id", { length: 19 }).primaryKey(),
|
id: varchar("id", { length: 20 }).primaryKey(),
|
||||||
|
timezone: text("timezone").notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const guildsRelations = relations(guilds, ({ one, many }) => ({
|
export const guildsRelations = relations(guilds, ({ one, many }) => ({
|
||||||
|
@ -99,41 +99,42 @@ export const guildsRelations = relations(guilds, ({ one, many }) => ({
|
||||||
|
|
||||||
export const timePlannings = pgTable("time_planning", {
|
export const timePlannings = pgTable("time_planning", {
|
||||||
id: serial("id").primaryKey(),
|
id: serial("id").primaryKey(),
|
||||||
guildId: varchar("guild_id", { length: 19 })
|
guildId: varchar("guild_id", { length: 20 })
|
||||||
|
.notNull()
|
||||||
|
.unique()
|
||||||
.references(() => guilds.id, {
|
.references(() => guilds.id, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
})
|
}),
|
||||||
.notNull()
|
channelId: varchar("channel_id", { length: 20 }).notNull(),
|
||||||
.unique(),
|
target_interval: smallint("target_interval").notNull(),
|
||||||
channelId: varchar("channel_id", { length: 19 }).notNull(),
|
isAvailableRoleId: varchar("is_available_role_id", { length: 20 }),
|
||||||
targetWeekday: smallint("target_weekday").notNull(),
|
|
||||||
targetHour: smallint("target_hour").notNull(),
|
|
||||||
targetMinute: smallint("target_minute").notNull(),
|
|
||||||
isAvailableRoleId: varchar("is_available_role_id", { length: 19 }),
|
|
||||||
wantsToBeNotifieRoledId: varchar("wants_to_be_notified_role_id", {
|
wantsToBeNotifieRoledId: varchar("wants_to_be_notified_role_id", {
|
||||||
length: 19,
|
length: 20,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const timePlanningsRelations = relations(
|
export const timePlanningsRelations = relations(
|
||||||
timePlannings,
|
timePlannings,
|
||||||
({ one, many }) => ({
|
({ one, many }) => ({
|
||||||
guild: one(tpMessages),
|
guild: one(guilds, {
|
||||||
|
fields: [timePlannings.guildId],
|
||||||
|
references: [guilds.id],
|
||||||
|
}),
|
||||||
messages: many(tpMessages),
|
messages: many(tpMessages),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const tpMessages = pgTable("tp_message", {
|
export const tpMessages = pgTable("tp_message", {
|
||||||
messageId: varchar("message_id", { length: 19 }).primaryKey(),
|
messageId: varchar("message_id", { length: 20 }).primaryKey(),
|
||||||
day: smallint("day").notNull(),
|
day: smallint("day").notNull(),
|
||||||
planId: varchar("plan_id", { length: 19 })
|
planId: integer("plan_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => timePlannings.guildId, { onDelete: "cascade" }),
|
.references(() => timePlannings.id, { onDelete: "cascade" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const tpMessagesRelations = relations(tpMessages, ({ one }) => ({
|
export const tpMessagesRelations = relations(tpMessages, ({ one }) => ({
|
||||||
timePlanning: one(timePlannings, {
|
plan: one(timePlannings, {
|
||||||
fields: [tpMessages.messageId],
|
fields: [tpMessages.planId],
|
||||||
references: [timePlannings.channelId],
|
references: [timePlannings.id],
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { createHandler, StartServer } from "@solidjs/start/server";
|
import { createHandler } from "@solidjs/start/entry";
|
||||||
|
import { StartServer } from "@solidjs/start/server";
|
||||||
|
|
||||||
export default createHandler(() => (
|
export default createHandler(() => (
|
||||||
<StartServer
|
<StartServer
|
||||||
|
@ -7,7 +8,7 @@ export default createHandler(() => (
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" href="/assets/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
{assets}
|
{assets}
|
||||||
</head>
|
</head>
|
||||||
<body id="app">
|
<body id="app">
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Title } from "@solidjs/meta";
|
import { Title } from "@solidjs/meta";
|
||||||
import { HttpStatusCode } from "@solidjs/start";
|
import { HttpStatusCode } from "@solidjs/start";
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/index.scss";
|
import "../styles/pages/index.scss";
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="index">
|
||||||
<Title>Not Found</Title>
|
<Title>Not Found</Title>
|
||||||
<HttpStatusCode code={404} />
|
<HttpStatusCode code={404} />
|
||||||
<section class="index">
|
<section class="index">
|
||||||
|
@ -17,6 +18,6 @@ export default function NotFound() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/about.scss";
|
import "../styles/pages/about.scss";
|
||||||
|
|
||||||
function about() {
|
function about() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="about">
|
||||||
<div class="aboutdiv">
|
|
||||||
<h1>About</h1>
|
<h1>About</h1>
|
||||||
<section>
|
<section>
|
||||||
<h2>Why does this bot exist?</h2>
|
<h2>Why does this bot exist?</h2>
|
||||||
|
@ -12,19 +12,19 @@ function about() {
|
||||||
<a href="/assets/screenshots/oldplanningmsg.png" target="_blank">
|
<a href="/assets/screenshots/oldplanningmsg.png" target="_blank">
|
||||||
these planning messages
|
these planning messages
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
and I thought that this should be automated. Some time later the
|
and I thought that this should be automated. Some time later the first
|
||||||
first version of li'l Judd was born. Today the bot has more features
|
version of li'l Judd was born. Today the bot has more features and
|
||||||
and keeps getting more of them! It is designed to actually improve
|
keeps getting more of them! It is designed to actually improve the
|
||||||
the Splatoon experience and not be the 10000th moderation and
|
Splatoon experience and not be the 10000th moderation and general
|
||||||
general utility bot with the same features as all bots.
|
utility bot with the same features as all bots.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Who is behind this?</h2>
|
<h2>Who is behind this?</h2>
|
||||||
<p>
|
<p>
|
||||||
The bot is currently being developed by{" "}
|
The bot is currently being developed by{" "}
|
||||||
<a href="/contact">moonleay</a> (hey that's me!) with
|
<a href="/contact">moonleay</a> (hey that's me!) with occasional
|
||||||
occasional help from his friends!
|
help from his friends!
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
@ -36,17 +36,17 @@ function about() {
|
||||||
<a href="https://git.moonleay.net/DiscordBots/lilJudd">
|
<a href="https://git.moonleay.net/DiscordBots/lilJudd">
|
||||||
read the code
|
read the code
|
||||||
</a>
|
</a>
|
||||||
and if you still don't trust me, you can always host the bot
|
and if you still don't trust me, you can always host the bot yourself!
|
||||||
yourself! A guide on how to do that can be found in the README of
|
A guide on how to do that can be found in the README of the git
|
||||||
the git project.
|
project.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Where is my data stored?</h2>
|
<h2>Where is my data stored?</h2>
|
||||||
<p>
|
<p>
|
||||||
Your data is stored on a VPS from Contabo in Germany. The bot used
|
Your data is stored on a VPS from Contabo in Germany. The bot used to
|
||||||
to be hosted on a server in my basement, but I moved it to a VPS,
|
be hosted on a server in my basement, but I moved it to a VPS, because
|
||||||
because my internet connection was not stable enough.
|
my internet connection was not stable enough.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
@ -66,13 +66,12 @@ function about() {
|
||||||
<section>
|
<section>
|
||||||
<h2>Hey, there is this really cool idea I have! Can you add it?</h2>
|
<h2>Hey, there is this really cool idea I have! Can you add it?</h2>
|
||||||
<p>
|
<p>
|
||||||
Just message me! I can't promise anything, but I am always open to
|
Just message me! I can't promise anything, but I am always open to new
|
||||||
new ideas and improvements! You can find ways to contact me{" "}
|
ideas and improvements! You can find ways to contact me{" "}
|
||||||
<a href="/contact">here</a>.
|
<a href="/contact">here</a>.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</Layout>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/acknowledgements.scss";
|
import "../styles/pages/acknowledgements.scss";
|
||||||
|
|
||||||
function acknowledgements() {
|
function acknowledgements() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="acknowledgements">
|
||||||
<div class="acknowledgements">
|
|
||||||
<h1>Acknowledgements</h1>
|
<h1>Acknowledgements</h1>
|
||||||
<section>
|
<section>
|
||||||
<table>
|
<table>
|
||||||
|
@ -142,10 +142,7 @@ function acknowledgements() {
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a
|
<a href="https://github.com/JetBrains/Exposed" target="_blank">
|
||||||
href="https://github.com/JetBrains/Exposed"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
repo
|
repo
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
@ -192,8 +189,7 @@ function acknowledgements() {
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</Layout>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
import "../styles/pages/config.scss";
|
|
||||||
|
|
||||||
function config() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h3 class={"centered"}>Configure li'l Judd in</h3>
|
|
||||||
<div class={"config"}>
|
|
||||||
<div>
|
|
||||||
<div class={"horizontal centered"}>
|
|
||||||
<img class={"guildpfp"} src="https://cdn.discordapp.com/icons/1040502664506646548/bb5a51c4659cf47bdd942bb11e974da7.webp?size=240" alt="Server pfp" />
|
|
||||||
<h1>li'l Judds home base</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<article>
|
|
||||||
<h2>Timezone</h2>
|
|
||||||
<p>Set the timezone for your server.</p>
|
|
||||||
<label>
|
|
||||||
<select>
|
|
||||||
<option value="-1">UTC-1:00</option>
|
|
||||||
<option value="0">UTC+0:00</option>
|
|
||||||
<option value="1">UTC+1:00</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</article>
|
|
||||||
<section>
|
|
||||||
<div class={"centered"}>
|
|
||||||
<h2>Features</h2>
|
|
||||||
<p>Configure the features of the bot</p>
|
|
||||||
</div>
|
|
||||||
<article>
|
|
||||||
<div class={"horizontal"}>
|
|
||||||
<h3>Time Planning</h3>
|
|
||||||
<input type="checkbox" id="time planning" />
|
|
||||||
</div>
|
|
||||||
<label class={"horizontal"}>
|
|
||||||
<p class={"marg_right_5px"}>Target channel:</p>
|
|
||||||
<select>
|
|
||||||
<option value="id">Channel 1</option>
|
|
||||||
<option value="id">Channel 2</option>
|
|
||||||
<option value="id">Channel 3</option>
|
|
||||||
<option value="id">Channel 4</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<div class={"horizontal"}>
|
|
||||||
<h4>Enable pingable Roles</h4>
|
|
||||||
<input type="checkbox" id="pingableroles" />
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
<button>Apply</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default config;
|
|
271
src/routes/config/[guildId].tsx
Normal file
271
src/routes/config/[guildId].tsx
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
import { getSession } from "@auth/solid-start";
|
||||||
|
import { faToggleOff, faToggleOn } from "@fortawesome/pro-regular-svg-icons";
|
||||||
|
import { useNavigate, useParams } from "@solidjs/router";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import moment from "moment-timezone";
|
||||||
|
import createClient from "openapi-fetch";
|
||||||
|
import { Index, createEffect, createResource, createSignal } from "solid-js";
|
||||||
|
import { createStore } from "solid-js/store";
|
||||||
|
import { getRequestEvent } from "solid-js/web";
|
||||||
|
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
|
import db from "~/drizzle";
|
||||||
|
import { accounts } from "~/drizzle/schema";
|
||||||
|
import { authOptions } from "~/server/auth";
|
||||||
|
import { paths } from "~/types/discord";
|
||||||
|
import "../../styles/pages/config.scss";
|
||||||
|
|
||||||
|
const guessTZ = () => Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
|
|
||||||
|
const initialValue = (params: ReturnType<typeof useParams>) => ({
|
||||||
|
success: null as boolean | null,
|
||||||
|
guild: {
|
||||||
|
id: params.guildId,
|
||||||
|
name: undefined as string | undefined,
|
||||||
|
icon: undefined as string | null | undefined,
|
||||||
|
},
|
||||||
|
tzNames: [guessTZ()],
|
||||||
|
});
|
||||||
|
|
||||||
|
const getPayload = async (
|
||||||
|
id: string,
|
||||||
|
): Promise<
|
||||||
|
| { success: false; message: string }
|
||||||
|
| (ReturnType<typeof initialValue> & { success: true })
|
||||||
|
> => {
|
||||||
|
"use server";
|
||||||
|
const event = getRequestEvent();
|
||||||
|
if (!event) return { success: false, message: "No request event!" };
|
||||||
|
|
||||||
|
const session = await getSession(event.request, authOptions);
|
||||||
|
if (!session?.user?.id)
|
||||||
|
return { success: false, message: "No user with id!" };
|
||||||
|
|
||||||
|
const { DISCORD_ACCESS_TOKEN } = (
|
||||||
|
await db
|
||||||
|
.selectDistinct({ DISCORD_ACCESS_TOKEN: accounts.access_token })
|
||||||
|
.from(accounts)
|
||||||
|
.where(eq(accounts.userId, session.user?.id))
|
||||||
|
.limit(1)
|
||||||
|
.execute()
|
||||||
|
)[0];
|
||||||
|
if (!DISCORD_ACCESS_TOKEN)
|
||||||
|
return { success: false, message: "No discord access token!" };
|
||||||
|
|
||||||
|
// const guilds = await fetch("https://discord.com/api/users/@me/guilds", {
|
||||||
|
// headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
|
||||||
|
// }).then((res) => res.json());
|
||||||
|
const { GET } = createClient<paths>({
|
||||||
|
baseUrl: "https://discord.com/api/v10",
|
||||||
|
});
|
||||||
|
const { data: guilds, error } = await GET("/users/@me/guilds", {
|
||||||
|
headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
|
return { success: false, message: "Error on discord api request!" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const guild = guilds?.find((e) => e.id === id);
|
||||||
|
|
||||||
|
if (!guild)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "User is in no such guild with requested id!",
|
||||||
|
};
|
||||||
|
if (!(parseInt(guild.permissions) & (1 << 5)))
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message:
|
||||||
|
"User is no MANAGE_GUILD permissions on this guild with requested id!",
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
guild: {
|
||||||
|
id: guild.id,
|
||||||
|
name: guild.name,
|
||||||
|
icon: guild.icon,
|
||||||
|
},
|
||||||
|
// guild: guilds
|
||||||
|
// .filter((e: any) => e.permissions & (1 << 5))
|
||||||
|
// .map((e: any) => e.name),
|
||||||
|
tzNames: moment.tz.names(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function config() {
|
||||||
|
const params = useParams();
|
||||||
|
const navigator = useNavigate();
|
||||||
|
let timezoneRef: HTMLInputElement;
|
||||||
|
let timePlanningRef: HTMLInputElement;
|
||||||
|
let pingableRolesRef: HTMLInputElement;
|
||||||
|
|
||||||
|
const [timezone, setTimezone] = createSignal(guessTZ());
|
||||||
|
const [payload] = createResource(params.guildId, async (id) => {
|
||||||
|
const payload = await getPayload(id);
|
||||||
|
|
||||||
|
if (!payload.success) {
|
||||||
|
console.log(payload.message, "No success");
|
||||||
|
navigator("/config", { replace: false });
|
||||||
|
return initialValue(params);
|
||||||
|
}
|
||||||
|
return payload;
|
||||||
|
});
|
||||||
|
const guild = () => payload()?.guild ?? initialValue(params).guild;
|
||||||
|
const tzNames = () => payload()?.tzNames ?? [];
|
||||||
|
const [config, setConfig] = createStore({
|
||||||
|
features: {
|
||||||
|
timePlanning: {
|
||||||
|
enabled: false,
|
||||||
|
pingableRoles: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
createEffect(() => console.log(payload()));
|
||||||
|
createEffect(() => console.log("timezone", timezone()));
|
||||||
|
createEffect(() =>
|
||||||
|
console.log("timePlanning.enabled", config.features.timePlanning.enabled),
|
||||||
|
);
|
||||||
|
createEffect(() =>
|
||||||
|
console.log(
|
||||||
|
"timePlanning.pingableRoles",
|
||||||
|
config.features.timePlanning.pingableRoles,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
createEffect(() => (timezoneRef.value = timezone()));
|
||||||
|
createEffect(
|
||||||
|
() => (timePlanningRef.checked = config.features.timePlanning.enabled),
|
||||||
|
);
|
||||||
|
createEffect(
|
||||||
|
() =>
|
||||||
|
(pingableRolesRef.checked = config.features.timePlanning.pingableRoles),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout site="config">
|
||||||
|
<h3 class="text-center">Configure li'l Judd in</h3>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<div class="flex-row centered">
|
||||||
|
<img
|
||||||
|
class="guildpfp"
|
||||||
|
src={
|
||||||
|
guild()?.icon
|
||||||
|
? `https://cdn.discordapp.com/icons/${guild()?.id}/${guild()
|
||||||
|
?.icon}.webp?size=240`
|
||||||
|
: "https://cdn.discordapp.com/icons/1040502664506646548/bb5a51c4659cf47bdd942bb11e974da7.webp?size=240"
|
||||||
|
}
|
||||||
|
alt="Server pfp"
|
||||||
|
/>
|
||||||
|
<h1>{guild()?.name ?? "li'l Judds home base"}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Guild</h2>
|
||||||
|
<p>General settings for this guild.</p>
|
||||||
|
<div class="flex-row">
|
||||||
|
<label for="timezone">Timezone for your server:</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
list="timezones"
|
||||||
|
id="timezone"
|
||||||
|
ref={timezoneRef!}
|
||||||
|
// disabled={!tzNames().find((e) => e === timezone())}
|
||||||
|
onInput={(e) => setTimezone(e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<datalist id="timezones">
|
||||||
|
<Index each={tzNames()}>
|
||||||
|
{(zone) => <option value={zone()} />}
|
||||||
|
</Index>
|
||||||
|
</datalist>
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={guessTZ() === timezone()}
|
||||||
|
title={"Detected: " + guessTZ()}
|
||||||
|
onClick={() => setTimezone(guessTZ())}
|
||||||
|
>
|
||||||
|
Auto-detect
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Features</h2>
|
||||||
|
<p>Configure the features of the bot</p>
|
||||||
|
<label for="timePlanning" class="flex-row">
|
||||||
|
<p>Time Planning </p>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={
|
||||||
|
config.features.timePlanning.enabled ? faToggleOn : faToggleOff
|
||||||
|
}
|
||||||
|
size="xl"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
hidden
|
||||||
|
type="checkbox"
|
||||||
|
id="timePlanning"
|
||||||
|
ref={timePlanningRef!}
|
||||||
|
onInput={(e) =>
|
||||||
|
setConfig("features", "timePlanning", "enabled", e.target.checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="sub"
|
||||||
|
classList={{ disabled: !config.features.timePlanning.enabled }}
|
||||||
|
>
|
||||||
|
<div class="flex-row">
|
||||||
|
<label>Target channel:</label>
|
||||||
|
<select value={timezone()}>
|
||||||
|
<optgroup label="--Select a Channel--">
|
||||||
|
<Index each={tzNames()}>
|
||||||
|
{(channel) => <option>{channel()}</option>}
|
||||||
|
</Index>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex-row">
|
||||||
|
<label for="pingableRoles" class="flex-row">
|
||||||
|
<p>Enable pingable Roles:</p>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={
|
||||||
|
config.features.timePlanning.pingableRoles
|
||||||
|
? faToggleOn
|
||||||
|
: faToggleOff
|
||||||
|
}
|
||||||
|
size="xl"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
hidden
|
||||||
|
type="checkbox"
|
||||||
|
id="pingableRoles"
|
||||||
|
ref={pingableRolesRef!}
|
||||||
|
onInput={(e) =>
|
||||||
|
setConfig(
|
||||||
|
"features",
|
||||||
|
"timePlanning",
|
||||||
|
"pingableRoles",
|
||||||
|
e.target.checked,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<button>Apply</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config;
|
124
src/routes/config/index.tsx
Normal file
124
src/routes/config/index.tsx
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import { getSession } from "@auth/solid-start";
|
||||||
|
import {
|
||||||
|
faBadgeCheck,
|
||||||
|
faCircleExclamation,
|
||||||
|
faPlus,
|
||||||
|
} from "@fortawesome/pro-regular-svg-icons";
|
||||||
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import createClient from "openapi-fetch";
|
||||||
|
import { For, createResource } from "solid-js";
|
||||||
|
import { getRequestEvent } from "solid-js/web";
|
||||||
|
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
|
import db from "~/drizzle";
|
||||||
|
import { accounts } from "~/drizzle/schema";
|
||||||
|
import { authOptions } from "~/server/auth";
|
||||||
|
import { paths } from "~/types/discord";
|
||||||
|
import "../../styles/pages/config.scss";
|
||||||
|
|
||||||
|
const initialValue = () => ({
|
||||||
|
success: null as boolean | null,
|
||||||
|
guilds: [] as {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon: string | null | undefined;
|
||||||
|
}[],
|
||||||
|
});
|
||||||
|
|
||||||
|
const getPayload = async (): Promise<
|
||||||
|
| { success: false; message: string }
|
||||||
|
| (ReturnType<typeof initialValue> & { success: true })
|
||||||
|
> => {
|
||||||
|
("use server");
|
||||||
|
const event = getRequestEvent();
|
||||||
|
if (!event) return { success: false, message: "No request event!" };
|
||||||
|
|
||||||
|
const session = await getSession(event.request, authOptions);
|
||||||
|
if (!session?.user?.id)
|
||||||
|
return { success: false, message: "No user with id!" };
|
||||||
|
|
||||||
|
const { DISCORD_ACCESS_TOKEN } = (
|
||||||
|
await db
|
||||||
|
.selectDistinct({ DISCORD_ACCESS_TOKEN: accounts.access_token })
|
||||||
|
.from(accounts)
|
||||||
|
.where(eq(accounts.userId, session.user?.id))
|
||||||
|
.limit(1)
|
||||||
|
.execute()
|
||||||
|
)[0];
|
||||||
|
if (!DISCORD_ACCESS_TOKEN)
|
||||||
|
return { success: false, message: "No discord access token!" };
|
||||||
|
|
||||||
|
const { GET } = createClient<paths>({
|
||||||
|
baseUrl: "https://discord.com/api/v10",
|
||||||
|
});
|
||||||
|
const { data: guilds, error } = await GET("/users/@me/guilds", {
|
||||||
|
headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("guilds", guilds);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log(error);
|
||||||
|
return { success: false, message: "Error on discord api request!" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
guilds:
|
||||||
|
guilds
|
||||||
|
?.filter((e) => parseInt(e.permissions) & (1 << 5))
|
||||||
|
.map(({ id, name, icon }) => ({ id, name, icon })) ?? [],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function index() {
|
||||||
|
const navigator = useNavigate();
|
||||||
|
|
||||||
|
const [payload] = createResource(async () => {
|
||||||
|
const payload = await getPayload();
|
||||||
|
|
||||||
|
if (!payload.success) {
|
||||||
|
console.log(payload.message, "No success");
|
||||||
|
navigator("/", { replace: false });
|
||||||
|
return initialValue();
|
||||||
|
}
|
||||||
|
console.log("success");
|
||||||
|
return payload;
|
||||||
|
});
|
||||||
|
|
||||||
|
const icons = [faPlus, faCircleExclamation, faBadgeCheck];
|
||||||
|
const colors = [undefined, "orange", "green"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout site="config">
|
||||||
|
<h3 class="text-center">Configure li'l Judd in</h3>
|
||||||
|
<div>
|
||||||
|
<For each={payload()?.guilds ?? []}>
|
||||||
|
{(guild, i) => (
|
||||||
|
<a href={`/config/${guild.id}`} class="flex-row centered">
|
||||||
|
<img
|
||||||
|
class="guildpfp"
|
||||||
|
src={
|
||||||
|
guild.icon
|
||||||
|
? `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.webp?size=240`
|
||||||
|
: "https://cdn.discordapp.com/icons/1040502664506646548/bb5a51c4659cf47bdd942bb11e974da7.webp?size=240"
|
||||||
|
}
|
||||||
|
alt="Server pfp"
|
||||||
|
/>
|
||||||
|
<h1>{guild.name}</h1>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
// beat={i() % 3 === 1}
|
||||||
|
color={colors[i() % 3]}
|
||||||
|
icon={icons[i() % 3]}
|
||||||
|
size="xl"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default index;
|
|
@ -1,19 +1,16 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/contact.scss";
|
import "../styles/pages/contact.scss";
|
||||||
|
|
||||||
function contact() {
|
function contact() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="contact">
|
||||||
<div class="contact">
|
|
||||||
<h1>Contact</h1>
|
<h1>Contact</h1>
|
||||||
<section class="contact">
|
<section>
|
||||||
<a href="mailto:contact@moonleay.net" target="_blank">
|
<a href="mailto:contact@moonleay.net" target="_blank">
|
||||||
<img src="/assets/icons/email.svg" alt="Email" />
|
<img src="/assets/icons/email.svg" alt="Email" />
|
||||||
contact@moonleay.net
|
contact@moonleay.net
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href="https://discord.com/users/372703841151614976" target="_blank">
|
||||||
href="https://discord.com/users/372703841151614976"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<img src="/assets/icons/discord.svg" alt="Discord" />
|
<img src="/assets/icons/discord.svg" alt="Discord" />
|
||||||
@moonleay
|
@moonleay
|
||||||
</a>
|
</a>
|
||||||
|
@ -22,8 +19,7 @@ function contact() {
|
||||||
li'l Judd's home base
|
li'l Judd's home base
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</Layout>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageSection from "~/components/ImageSection";
|
import ImageSection from "~/components/ImageSection";
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/features.scss";
|
import "../styles/pages/features.scss";
|
||||||
|
|
||||||
function features() {
|
function features() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="features">
|
||||||
<div class="features">
|
<h1>Features</h1>
|
||||||
<h1 class="title">Features</h1>
|
|
||||||
<div class="gridlayout">
|
<div class="gridlayout">
|
||||||
<ImageSection
|
<ImageSection
|
||||||
imgUrl="/assets/screenshots/timeplanner.png"
|
imgUrl="/assets/screenshots/timeplanner.png"
|
||||||
|
@ -44,8 +44,7 @@ function features() {
|
||||||
note="If you have a specific feature request, you can contact me on Discord: @moonleay or email: contact at moonleay dot net"
|
note="If you have a specific feature request, you can contact me on Discord: @moonleay or email: contact at moonleay dot net"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/how-do-i.scss";
|
import "../styles/pages/how-do-i.scss";
|
||||||
|
|
||||||
function howDoI() {
|
function howDoI() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="how-do-i">
|
||||||
<h1 class="hdi-title">How do I...?</h1>
|
<h1>How do I...?</h1>
|
||||||
<section class="hdi-section">
|
<section>
|
||||||
<h2>.. enable / disable certain features?</h2>
|
<h2>.. enable / disable certain features?</h2>
|
||||||
<p>
|
<p>
|
||||||
Features can be enabled and disables using the <code>/feature</code>
|
Features can be enabled and disables using the <code>/feature</code>
|
||||||
|
@ -26,7 +27,7 @@ function howDoI() {
|
||||||
{/* <p><code>/feature feature:Time Planning Feature set:Enable channel:#ich-kann-heute</code></p> */}
|
{/* <p><code>/feature feature:Time Planning Feature set:Enable channel:#ich-kann-heute</code></p> */}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="hdi-section">
|
<section>
|
||||||
<h2>.. create a match?</h2>
|
<h2>.. create a match?</h2>
|
||||||
<p>
|
<p>
|
||||||
You can create a match time using the <code>/match</code> command.
|
You can create a match time using the <code>/match</code> command.
|
||||||
|
@ -47,14 +48,14 @@ function howDoI() {
|
||||||
{/* <p><code>/match match:Ladder Match timestamp:24.12.2069 04:20 opponent:Forbidden</code></p> */}
|
{/* <p><code>/match match:Ladder Match timestamp:24.12.2069 04:20 opponent:Forbidden</code></p> */}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="hdi-footernotesection">
|
<section class="note">
|
||||||
<p>
|
<p>
|
||||||
Is something missing here?
|
Is something missing here?
|
||||||
<br />
|
<br />
|
||||||
Please <a href="/contact">contact me</a>!
|
Please <a href="/contact">contact me</a>!
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/imprint.scss";
|
import "../styles/pages/imprint.scss";
|
||||||
|
|
||||||
function imprint() {
|
function imprint() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="imprint">
|
||||||
<section class="imprint">
|
<section>
|
||||||
<h1>Imprint</h1>
|
<h1>Imprint</h1>
|
||||||
<section>
|
<section>
|
||||||
<a href="/contact">
|
<a href="/contact">
|
||||||
|
@ -54,7 +55,7 @@ function imprint() {
|
||||||
</h5>
|
</h5>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/index.scss";
|
import "../styles/pages/index.scss";
|
||||||
|
|
||||||
function index() {
|
function index() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="index">
|
||||||
<section class="index">
|
<section>
|
||||||
<h1>li'l Judd</h1>
|
<h1>li'l Judd</h1>
|
||||||
<h5>The competetive Splatoon Bot</h5>
|
<h5>The competetive Splatoon Bot</h5>
|
||||||
<div>
|
<div>
|
||||||
|
@ -13,7 +14,7 @@ function index() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/privacy-policy.scss";
|
import "../styles/pages/privacy-policy.scss";
|
||||||
|
|
||||||
function privacyPolicy() {
|
function privacyPolicy() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="privacyPolicy">
|
||||||
<div class="privacyPolicy">
|
|
||||||
<div>
|
<div>
|
||||||
<h1>Privacy Policy for li'l Judd</h1>
|
<h1>Privacy Policy for li'l Judd</h1>
|
||||||
<h4>Last updated: 2023-12-05</h4>
|
<h4>Last updated: 2023-12-05</h4>
|
||||||
|
@ -57,8 +57,8 @@ function privacyPolicy() {
|
||||||
</ul>
|
</ul>
|
||||||
<h3>3.2 Usage Data</h3>
|
<h3>3.2 Usage Data</h3>
|
||||||
<p>
|
<p>
|
||||||
We may collect information on how you interact with our bot,
|
We may collect information on how you interact with our bot, including
|
||||||
including but not limited to:
|
but not limited to:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
@ -96,8 +96,8 @@ function privacyPolicy() {
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
- Consent: You have given your consent for the processing of
|
- Consent: You have given your consent for the processing of your
|
||||||
your personal data for one or more specific purposes.
|
personal data for one or more specific purposes.
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -111,9 +111,8 @@ function privacyPolicy() {
|
||||||
<section>
|
<section>
|
||||||
<h2>6. Data Sharing</h2>
|
<h2>6. Data Sharing</h2>
|
||||||
<p>
|
<p>
|
||||||
We do not sell, trade, or otherwise transfer your personal
|
We do not sell, trade, or otherwise transfer your personal information
|
||||||
information to third parties. However, we may share your information
|
to third parties. However, we may share your information with:
|
||||||
with:
|
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
@ -151,8 +150,8 @@ function privacyPolicy() {
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>
|
<p>
|
||||||
- Right to erasure: You can request the deletion of your
|
- Right to erasure: You can request the deletion of your personal
|
||||||
personal data.
|
data.
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -160,8 +159,8 @@ function privacyPolicy() {
|
||||||
<section>
|
<section>
|
||||||
<h2>9. Changes to this Privacy Policy</h2>
|
<h2>9. Changes to this Privacy Policy</h2>
|
||||||
<p>
|
<p>
|
||||||
We may update this Privacy Policy to reflect changes in our
|
We may update this Privacy Policy to reflect changes in our practices.
|
||||||
practices. The updated version will be posted on
|
The updated version will be posted on
|
||||||
https://liljudd.ink/privacy-policy.
|
https://liljudd.ink/privacy-policy.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
@ -172,8 +171,7 @@ function privacyPolicy() {
|
||||||
please contact us at contact@moonleay.net.
|
please contact us at contact@moonleay.net.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</Layout>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/stack.scss";
|
import "../styles/pages/stack.scss";
|
||||||
|
|
||||||
function stack() {
|
function stack() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="stack">
|
||||||
<h1 class="stack-title">The Stack</h1>
|
<h1>The Stack</h1>
|
||||||
<section class="stack-section">
|
<section>
|
||||||
<img src="/assets/logos/kotlin.svg" alt="Kotlin 'K' logo" />
|
<img src="/assets/logos/kotlin.svg" alt="Kotlin 'K' logo" />
|
||||||
<div class="stackgrid_3 stackitm">
|
<div class="stackgrid_3 stackitm">
|
||||||
<h1>The Kotlin programming language</h1>
|
<h1>The Kotlin programming language</h1>
|
||||||
|
@ -14,14 +15,14 @@ function stack() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="stack-section">
|
<section>
|
||||||
<img src="/assets/logos/kord.png" alt="The Kord logo" />
|
<img src="/assets/logos/kord.png" alt="The Kord logo" />
|
||||||
<div class="stackgrid_3 stackitm">
|
<div class="stackgrid_3 stackitm">
|
||||||
<h1>The Kord library</h1>
|
<h1>The Kord library</h1>
|
||||||
<p>A Kotlin library for creating Discord bots. Pretty bare bones.</p>
|
<p>A Kotlin library for creating Discord bots. Pretty bare bones.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="stack-section">
|
<section>
|
||||||
<img
|
<img
|
||||||
src="/assets/logos/kordextensions.png"
|
src="/assets/logos/kordextensions.png"
|
||||||
alt="The Kord-Extensions logo"
|
alt="The Kord-Extensions logo"
|
||||||
|
@ -31,7 +32,7 @@ function stack() {
|
||||||
<p>A Kotlin library to improve the Kord experience.</p>
|
<p>A Kotlin library to improve the Kord experience.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="stack-section">
|
<section>
|
||||||
<img src="/assets/logos/pgelephant.png" alt="The PostgreSQL elephant" />
|
<img src="/assets/logos/pgelephant.png" alt="The PostgreSQL elephant" />
|
||||||
<div class="stackgrid_3 stackitm">
|
<div class="stackgrid_3 stackitm">
|
||||||
<h1>The PostgreSQL database</h1>
|
<h1>The PostgreSQL database</h1>
|
||||||
|
@ -47,7 +48,7 @@ function stack() {
|
||||||
<a href="/acknowledgements">Acknowledgements</a>.
|
<a href="/acknowledgements">Acknowledgements</a>.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
|
import Layout from "~/components/Layout";
|
||||||
import "../styles/pages/terms-of-service.scss";
|
import "../styles/pages/terms-of-service.scss";
|
||||||
|
|
||||||
function termsOfService() {
|
function termsOfService() {
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout site="termsOfService">
|
||||||
<div class="termsOfService">
|
|
||||||
<h1>Terms of Service</h1>
|
<h1>Terms of Service</h1>
|
||||||
<div>
|
<div>
|
||||||
<h2>Usage Agreement</h2>
|
<h2>Usage Agreement</h2>
|
||||||
<p>
|
<p>
|
||||||
By inviting the bot and using its features (commands, planning
|
By inviting the bot and using its features (commands, planning system)
|
||||||
system) are you agreeing to the below mentioned Terms and Privacy
|
are you agreeing to the below mentioned Terms and Privacy Policy
|
||||||
Policy (Policy) of the bot.
|
(Policy) of the bot.
|
||||||
<br />
|
<br />
|
||||||
You acknowledge that you have the privilege to use the bot freely on
|
You acknowledge that you have the privilege to use the bot freely on
|
||||||
any Discord Server (Server) you share with it, that you can invite
|
any Discord Server (Server) you share with it, that you can invite it
|
||||||
it to any Server that you have "Manage Server" rights for and that
|
to any Server that you have "Manage Server" rights for and that this
|
||||||
this privilege might get revoked for you, if you're subject of
|
privilege might get revoked for you, if you're subject of breaking the
|
||||||
breaking the terms and/or policy of this bot, or the{" "}
|
terms and/or policy of this bot, or the{" "}
|
||||||
<a href="https://discord.com/terms" target="_blank">
|
<a href="https://discord.com/terms" target="_blank">
|
||||||
Terms of Service
|
Terms of Service
|
||||||
</a>
|
</a>
|
||||||
|
@ -33,11 +33,11 @@ function termsOfService() {
|
||||||
Discord Inc
|
Discord Inc
|
||||||
</a>
|
</a>
|
||||||
.<br />
|
.<br />
|
||||||
Through Inviting the bot may it collect specific data as described
|
Through Inviting the bot may it collect specific data as described in
|
||||||
in its Policy.
|
its Policy.
|
||||||
<br />
|
<br />
|
||||||
The intended usage of this data is for core functionalities of the
|
The intended usage of this data is for core functionalities of the bot
|
||||||
bot such as command handling, guild-specific settings and the
|
such as command handling, guild-specific settings and the
|
||||||
time-planning system.
|
time-planning system.
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
|
@ -45,11 +45,11 @@ function termsOfService() {
|
||||||
<div>
|
<div>
|
||||||
<h2>Intended Age</h2>
|
<h2>Intended Age</h2>
|
||||||
<p>
|
<p>
|
||||||
The bot may not be used by individuals under the minimal age
|
The bot may not be used by individuals under the minimal age described
|
||||||
described in Discord's Terms of Service.
|
in Discord's Terms of Service.
|
||||||
<br />
|
<br />
|
||||||
Doing so will be seen as a violation of these terms and will result
|
Doing so will be seen as a violation of these terms and will result in
|
||||||
in a removal of the bot from any servers you own.
|
a removal of the bot from any servers you own.
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,8 +60,7 @@ function termsOfService() {
|
||||||
<br />
|
<br />
|
||||||
Any direct connection to Discord or any of its Trademark objects is
|
Any direct connection to Discord or any of its Trademark objects is
|
||||||
purely coincidental. We do not claim to have the copyright ownership
|
purely coincidental. We do not claim to have the copyright ownership
|
||||||
of any of Discord's assets, trademarks or other intellectual
|
of any of Discord's assets, trademarks or other intellectual property.
|
||||||
property.
|
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,26 +70,25 @@ function termsOfService() {
|
||||||
The owner of the bot may not be made liable for individuals breaking
|
The owner of the bot may not be made liable for individuals breaking
|
||||||
these Terms at any given time.
|
these Terms at any given time.
|
||||||
<br />
|
<br />
|
||||||
He has faith in the end users being truthfull about their
|
He has faith in the end users being truthfull about their information
|
||||||
information and not misusing this bot or the services of Discord Inc
|
and not misusing this bot or the services of Discord Inc in a
|
||||||
in a malicious way.
|
malicious way.
|
||||||
<br />
|
<br />
|
||||||
We reserve the right to update these terms at our own discretion,
|
We reserve the right to update these terms at our own discretion,
|
||||||
giving you a 1-Week (7 days) period to opt out of these terms if
|
giving you a 1-Week (7 days) period to opt out of these terms if
|
||||||
you're not agreeing with the new changes. You may opt out by
|
you're not agreeing with the new changes. You may opt out by Removing
|
||||||
Removing the bot from any Server you have the rights for.
|
the bot from any Server you have the rights for.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2>Contact</h2>
|
<h2>Contact</h2>
|
||||||
<p>
|
<p>
|
||||||
People may get in contact through e-mail at contact@moonleay.net, or
|
People may get in contact through e-mail at contact@moonleay.net, or
|
||||||
through the official Support Discord of the Bot. Other ways of
|
through the official Support Discord of the Bot. Other ways of support
|
||||||
support may be provided but aren't guaranteed.
|
may be provided but aren't guaranteed.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,23 @@ export const authOptions: SolidAuthConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
...Discord({
|
...Discord({
|
||||||
clientId: process.env.DISCORD_CLIENT_ID,
|
clientId: import.meta.env.VITE_DISCORD_CLIENT_ID,
|
||||||
clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
clientSecret: import.meta.env.VITE_DISCORD_CLIENT_SECRET,
|
||||||
}),
|
}),
|
||||||
authorization:
|
authorization:
|
||||||
"https://discord.com/api/oauth2/authorize?scope=identify+email+guilds+guilds.members.read",
|
"https://discord.com/api/oauth2/authorize?scope=identify+email+guilds+guilds.members.read",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
adapter: DrizzleAdapter(db),
|
adapter: DrizzleAdapter(db),
|
||||||
secret: process.env.AUTH_SECRET,
|
secret: import.meta.env.VITE_AUTH_SECRET,
|
||||||
|
callbacks: {
|
||||||
|
session: ({ session, user }) => {
|
||||||
|
if (session?.user) {
|
||||||
|
session.user.id = user.id;
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
},
|
||||||
|
},
|
||||||
pages: {
|
pages: {
|
||||||
// signIn: "/signin",
|
// signIn: "/signin",
|
||||||
// signOut: "/signout",
|
// signOut: "/signout",
|
||||||
|
@ -23,4 +31,7 @@ export const authOptions: SolidAuthConfig = {
|
||||||
// verifyRequest: '/auth/verify-request', // (used for check email message)
|
// verifyRequest: '/auth/verify-request', // (used for check email message)
|
||||||
// newUser: '/auth/new-user' // New users will be directed here on first sign in (leave the property out if not of interest)
|
// newUser: '/auth/new-user' // New users will be directed here on first sign in (leave the property out if not of interest)
|
||||||
},
|
},
|
||||||
|
redirectProxyUrl: import.meta.env.DEV
|
||||||
|
? import.meta.env.VITE_AUTH_REDIRECT_PROXY_URL
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,46 +5,73 @@ nav {
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
ul {
|
ul .navElem a {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
.navElem {
|
|
||||||
margin: 0.5rem 1rem;
|
|
||||||
transition: 0.5s;
|
|
||||||
color: white;
|
color: white;
|
||||||
|
padding: 0 1rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: rgb(96 59 255) !important;
|
color: rgb(96 59 255) !important;
|
||||||
|
|
||||||
|
.swap {
|
||||||
|
svg path {
|
||||||
|
transition: color 0.5s 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textBx {
|
.primary {
|
||||||
display: flex;
|
opacity: 0;
|
||||||
align-items: center;
|
}
|
||||||
|
|
||||||
#logo {
|
.secondary {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
.swap,
|
||||||
|
.swap svg {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
max-width: initial;
|
max-width: initial;
|
||||||
max-height: initial;
|
max-height: initial;
|
||||||
margin: 0.5rem;
|
}
|
||||||
|
|
||||||
|
.swap {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
transition: opacity 0.5s;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span,
|
||||||
|
> svg path {
|
||||||
|
transition: color 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lower {
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
ul {
|
justify-content: space-between;
|
||||||
flex-direction: row;
|
|
||||||
padding: 0 2rem;
|
|
||||||
|
|
||||||
.navElem:last-child {
|
ul {
|
||||||
margin-left: auto;
|
height: 72px;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,3 +53,24 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
&.centered {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.thick {
|
||||||
|
align-items: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.responsive {
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
.aboutdiv {
|
.about {
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,38 +1,34 @@
|
||||||
|
.config {
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.guildpfp {
|
.guildpfp {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.horizontal {
|
label {
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
h1 {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
p, h3, h4 {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.marg_right_5px {
|
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.centered {
|
section,
|
||||||
text-align: center;
|
a {
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config {
|
|
||||||
article {
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub {
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
}
|
}
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.contact {
|
section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.title {
|
h1 {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 1.2rem;
|
margin-bottom: 1.2rem;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
.hdi-title {
|
.how-do-i {
|
||||||
|
h1 {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 1.2rem;
|
margin-bottom: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hdi-section {
|
section {
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -41,7 +42,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hdi-footernotesection {
|
.note {
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
margin: 1rem auto;
|
margin: 1rem auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -62,3 +63,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
.stack-title {
|
.stack {
|
||||||
|
h1 {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 1.2rem;
|
margin-bottom: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stack-section {
|
section {
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stack-note {
|
.note {
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
|
@ -59,3 +60,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
15
src/types/authjs.d.ts
vendored
Normal file
15
src/types/authjs.d.ts
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { DefaultSession as DSession } from "@auth/core/types"
|
||||||
|
|
||||||
|
declare module "@auth/core/types" {
|
||||||
|
/**
|
||||||
|
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
||||||
|
*/
|
||||||
|
interface Session extends DSession {
|
||||||
|
user?: {
|
||||||
|
id: string
|
||||||
|
name?: string | null
|
||||||
|
email?: string | null
|
||||||
|
image?: string | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7751
src/types/discord.d.ts
vendored
Normal file
7751
src/types/discord.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
16
src/types/env.d.ts
vendored
Normal file
16
src/types/env.d.ts
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/// <reference types="vinxi/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_DISCORD_CLIENT: string;
|
||||||
|
readonly VITE_DISCORD_CLIENT_SECRET: string;
|
||||||
|
readonly VITE_DISCORD_BOT_TOKEN: string;
|
||||||
|
|
||||||
|
readonly VITE_AUTH_SECRET: string;
|
||||||
|
readonly VITE_AUTH_REDIRECT_PROXY_URL: string | undefined;
|
||||||
|
|
||||||
|
readonly VITE_DATABASE_URL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
196
src/types/liljudd.d.ts
vendored
Normal file
196
src/types/liljudd.d.ts
vendored
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
/**
|
||||||
|
* This file was auto-generated by openapi-typescript.
|
||||||
|
* Do not make direct changes to the file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export interface paths {
|
||||||
|
"/api/config/{guildId}": {
|
||||||
|
/**
|
||||||
|
* Find guild config by ID
|
||||||
|
* @description Returns a single guild config
|
||||||
|
*/
|
||||||
|
get: operations["getGuildById"];
|
||||||
|
/**
|
||||||
|
* Deletes a guild config by ID
|
||||||
|
* @description Delete a guild's config
|
||||||
|
*/
|
||||||
|
delete: operations["deleteGuildById"];
|
||||||
|
};
|
||||||
|
"/api/tp_messages/{guildId}": {
|
||||||
|
/**
|
||||||
|
* Find guild by ID for it's tp_messages
|
||||||
|
* @description Returns tp_messages for a guild
|
||||||
|
*/
|
||||||
|
get: operations["getTp_messagesOfGuildById"];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type webhooks = Record<string, never>;
|
||||||
|
|
||||||
|
export interface components {
|
||||||
|
schemas: {
|
||||||
|
guildConfig: {
|
||||||
|
/**
|
||||||
|
* Format: varchar(19)
|
||||||
|
* @example 1234567890123456789
|
||||||
|
*/
|
||||||
|
guildID?: string;
|
||||||
|
features?: {
|
||||||
|
time_planning?: {
|
||||||
|
/**
|
||||||
|
* Format: varchar(19)
|
||||||
|
* @example 1234567890123456789
|
||||||
|
*/
|
||||||
|
channelID?: string;
|
||||||
|
/** @example 0 0 1 * * * 60o 1w */
|
||||||
|
cron?: string;
|
||||||
|
/**
|
||||||
|
* Format: varchar(19)
|
||||||
|
* @example 1234567890123456789
|
||||||
|
*/
|
||||||
|
isAvailableRoleId?: string;
|
||||||
|
/**
|
||||||
|
* Format: varchar(19)
|
||||||
|
* @example 1234567890123456789
|
||||||
|
*/
|
||||||
|
wantsToBeNotifieRoledId?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
matches?: components["schemas"]["match"][];
|
||||||
|
};
|
||||||
|
match: {
|
||||||
|
/**
|
||||||
|
* Format: varchar(19)
|
||||||
|
* @example 1234567890123456789
|
||||||
|
*/
|
||||||
|
channelID?: string;
|
||||||
|
/**
|
||||||
|
* Format: varchar(50)
|
||||||
|
* @example Scrim
|
||||||
|
*/
|
||||||
|
matchType?: string;
|
||||||
|
/**
|
||||||
|
* Format: varchar(19)
|
||||||
|
* @example 1234567890123456789
|
||||||
|
*/
|
||||||
|
createrId?: string;
|
||||||
|
/**
|
||||||
|
* Format: varchar(19)
|
||||||
|
* @example 1234567890123456789
|
||||||
|
*/
|
||||||
|
roleId?: string;
|
||||||
|
/**
|
||||||
|
* Format: varchar(100)
|
||||||
|
* @example ?
|
||||||
|
*/
|
||||||
|
opponentName?: string;
|
||||||
|
/**
|
||||||
|
* Format: varchar(19)
|
||||||
|
* @example 1234567890123456789
|
||||||
|
*/
|
||||||
|
messsageId?: string;
|
||||||
|
/** @example 0 0 1 5 2 2023 60o */
|
||||||
|
cron?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: never;
|
||||||
|
parameters: never;
|
||||||
|
requestBodies: never;
|
||||||
|
headers: never;
|
||||||
|
pathItems: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type $defs = Record<string, never>;
|
||||||
|
|
||||||
|
export type external = Record<string, never>;
|
||||||
|
|
||||||
|
export interface operations {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find guild config by ID
|
||||||
|
* @description Returns a single guild config
|
||||||
|
*/
|
||||||
|
getGuildById: {
|
||||||
|
parameters: {
|
||||||
|
path: {
|
||||||
|
/** @description ID of guild config to return */
|
||||||
|
guildId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description successful operation */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["guildConfig"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Invalid ID supplied */
|
||||||
|
400: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Guild not found */
|
||||||
|
404: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Deletes a guild config by ID
|
||||||
|
* @description Delete a guild's config
|
||||||
|
*/
|
||||||
|
deleteGuildById: {
|
||||||
|
parameters: {
|
||||||
|
path: {
|
||||||
|
/** @description ID of guild config to delete */
|
||||||
|
guildId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description successful operation */
|
||||||
|
204: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Invalid ID supplied */
|
||||||
|
400: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Guild not found */
|
||||||
|
404: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Find guild by ID for it's tp_messages
|
||||||
|
* @description Returns tp_messages for a guild
|
||||||
|
*/
|
||||||
|
getTp_messagesOfGuildById: {
|
||||||
|
parameters: {
|
||||||
|
path: {
|
||||||
|
/** @description ID of guild's tp_messages to return */
|
||||||
|
guildId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description successful operation */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["guildConfig"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Time planning not enabled for this guild */
|
||||||
|
204: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Invalid ID supplied */
|
||||||
|
400: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Guild not found */
|
||||||
|
404: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue