mirror of
https://github.com/ershisan99/flashcards-admin-bot.git
synced 2025-12-16 05:09:25 +00:00
add basic functionality for frontend, json db
This commit is contained in:
123
data/_data.json
Normal file
123
data/_data.json
Normal file
@@ -0,0 +1,123 @@
|
||||
{
|
||||
"rawData": {
|
||||
"558491754": {
|
||||
"name": "Ivan Ivanov",
|
||||
"availableTime": {
|
||||
"from": 40,
|
||||
"to": null
|
||||
},
|
||||
"tgUsername": "andrei_zadorozhnyi",
|
||||
"chatId": 558491754,
|
||||
"userId": "558491754"
|
||||
}
|
||||
},
|
||||
"groups": [
|
||||
[
|
||||
{
|
||||
"userId": "257862776",
|
||||
"name": "Катя Разуева",
|
||||
"availableTime": {
|
||||
"from": 10,
|
||||
"to": 20
|
||||
},
|
||||
"tgUsername": "ekate_25",
|
||||
"chatId": 257862776
|
||||
},
|
||||
{
|
||||
"userId": "320185869",
|
||||
"name": "Евгений Березкин",
|
||||
"availableTime": {
|
||||
"from": 10,
|
||||
"to": 20
|
||||
},
|
||||
"tgUsername": "breznovic",
|
||||
"chatId": 320185869
|
||||
},
|
||||
{
|
||||
"userId": "484619169",
|
||||
"name": "Владислав Беляев",
|
||||
"availableTime": {
|
||||
"from": 10,
|
||||
"to": 20
|
||||
},
|
||||
"tgUsername": "buladzislau",
|
||||
"chatId": 484619169
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"userId": "550022173",
|
||||
"name": "Садов Артем",
|
||||
"availableTime": {
|
||||
"from": 10,
|
||||
"to": 20
|
||||
},
|
||||
"tgUsername": "SadovArtem",
|
||||
"chatId": 550022173
|
||||
},
|
||||
{
|
||||
"userId": "1534716020",
|
||||
"name": "Игорь Шаргин",
|
||||
"availableTime": {
|
||||
"from": 10,
|
||||
"to": 20
|
||||
},
|
||||
"tgUsername": "Igor_Shargin",
|
||||
"chatId": 1534716020
|
||||
},
|
||||
{
|
||||
"userId": "178615283",
|
||||
"name": "Артур Ислакаев",
|
||||
"availableTime": {
|
||||
"from": 20,
|
||||
"to": 30
|
||||
},
|
||||
"tgUsername": "kven_a",
|
||||
"chatId": 178615283
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"userId": "1111257233",
|
||||
"name": "Александр Черемных",
|
||||
"availableTime": {
|
||||
"from": 20,
|
||||
"to": 30
|
||||
},
|
||||
"chatId": 1111257233
|
||||
},
|
||||
{
|
||||
"userId": "74330176",
|
||||
"name": "Герман Владислав",
|
||||
"availableTime": {
|
||||
"from": 30,
|
||||
"to": 40
|
||||
},
|
||||
"tgUsername": "vladloot",
|
||||
"chatId": 74330176
|
||||
},
|
||||
{
|
||||
"userId": "212999392",
|
||||
"name": "Листопад Георгий",
|
||||
"availableTime": {
|
||||
"from": 30,
|
||||
"to": 40
|
||||
},
|
||||
"tgUsername": "thencatchfinally",
|
||||
"chatId": 212999392
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"userId": "855517731",
|
||||
"name": "/add_to_team",
|
||||
"availableTime": {
|
||||
"from": 30,
|
||||
"to": 40
|
||||
},
|
||||
"tgUsername": "go_phase",
|
||||
"chatId": 855517731
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1 +1,48 @@
|
||||
{}
|
||||
{
|
||||
"rawData": {
|
||||
"424297319": {
|
||||
"name": "Зимин Владислав",
|
||||
"availableTime": {
|
||||
"from": 30,
|
||||
"to": 40
|
||||
},
|
||||
"tgUsername": "swq_2018",
|
||||
"chatId": 424297319,
|
||||
"userId": "424297319"
|
||||
},
|
||||
"558491754": {
|
||||
"name": "Andrei Zadorozhnyi",
|
||||
"availableTime": {
|
||||
"from": 30,
|
||||
"to": 40
|
||||
},
|
||||
"tgUsername": "andrei_zadorozhnyi",
|
||||
"chatId": 558491754,
|
||||
"userId": "558491754"
|
||||
}
|
||||
},
|
||||
"groups": [
|
||||
[
|
||||
{
|
||||
"name": "Зимин Владислав",
|
||||
"availableTime": {
|
||||
"from": 30,
|
||||
"to": 40
|
||||
},
|
||||
"tgUsername": "swq_2018",
|
||||
"chatId": 424297319,
|
||||
"userId": "424297319"
|
||||
},
|
||||
{
|
||||
"name": "Andrei Zadorozhnyi",
|
||||
"availableTime": {
|
||||
"from": 30,
|
||||
"to": 40
|
||||
},
|
||||
"tgUsername": "andrei_zadorozhnyi",
|
||||
"chatId": 558491754,
|
||||
"userId": "558491754"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
15
data/preregistered.json
Normal file
15
data/preregistered.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"rawData": {
|
||||
"424297319": {
|
||||
"name": "Зимин Владислав",
|
||||
"availableTime": {
|
||||
"from": 30,
|
||||
"to": 40
|
||||
},
|
||||
"tgUsername": "swq_2018",
|
||||
"chatId": 424297319,
|
||||
"userId": "424297319"
|
||||
}
|
||||
},
|
||||
"groups": []
|
||||
}
|
||||
13
front/.eslintignore
Normal file
13
front/.eslintignore
Normal file
@@ -0,0 +1,13 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
31
front/.eslintrc.cjs
Normal file
31
front/.eslintrc.cjs
Normal file
@@ -0,0 +1,31 @@
|
||||
/** @type { import("eslint").Linter.Config } */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:svelte/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
parser: 'svelte-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
10
front/.gitignore
vendored
Normal file
10
front/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
1
front/.npmrc
Normal file
1
front/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
engine-strict=true
|
||||
4
front/.prettierignore
Normal file
4
front/.prettierignore
Normal file
@@ -0,0 +1,4 @@
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
8
front/.prettierrc
Normal file
8
front/.prettierrc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
38
front/README.md
Normal file
38
front/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
||||
14
front/components.json
Normal file
14
front/components.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||
"style": "default",
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "src\\app.css",
|
||||
"baseColor": "slate"
|
||||
},
|
||||
"aliases": {
|
||||
"components": "$lib/components",
|
||||
"utils": "$lib/utils"
|
||||
},
|
||||
"typescript": true
|
||||
}
|
||||
43
front/package.json
Normal file
43
front/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "front",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@types/eslint": "^8.56.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"postcss": "^8.4.33",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"prettier-plugin-tailwindcss": "^0.5.1",
|
||||
"svelte": "^4.2.7",
|
||||
"svelte-check": "^3.6.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.3"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"bits-ui": "^0.21.9",
|
||||
"clsx": "^2.1.1",
|
||||
"tailwind-merge": "^2.3.0",
|
||||
"tailwind-variants": "^0.2.1"
|
||||
}
|
||||
}
|
||||
3011
front/pnpm-lock.yaml
generated
Normal file
3011
front/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
front/postcss.config.js
Normal file
6
front/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
||||
78
front/src/app.css
Normal file
78
front/src/app.css
Normal file
@@ -0,0 +1,78 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--destructive: 0 72.2% 50.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: hsl(212.7,26.8%,83.9);
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
13
front/src/app.d.ts
vendored
Normal file
13
front/src/app.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
13
front/src/app.html
Normal file
13
front/src/app.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
25
front/src/lib/components/ui/button/button.svelte
Normal file
25
front/src/lib/components/ui/button/button.svelte
Normal file
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { Button as ButtonPrimitive } from "bits-ui";
|
||||
import { type Events, type Props, buttonVariants } from "./index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = Props;
|
||||
type $$Events = Events;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let variant: $$Props["variant"] = "default";
|
||||
export let size: $$Props["size"] = "default";
|
||||
export let builders: $$Props["builders"] = [];
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<ButtonPrimitive.Root
|
||||
{builders}
|
||||
class={cn(buttonVariants({ variant, size, className }))}
|
||||
type="button"
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
>
|
||||
<slot />
|
||||
</ButtonPrimitive.Root>
|
||||
49
front/src/lib/components/ui/button/index.ts
Normal file
49
front/src/lib/components/ui/button/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { type VariantProps, tv } from "tailwind-variants";
|
||||
import type { Button as ButtonPrimitive } from "bits-ui";
|
||||
import Root from "./button.svelte";
|
||||
|
||||
const buttonVariants = tv({
|
||||
base: "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
});
|
||||
|
||||
type Variant = VariantProps<typeof buttonVariants>["variant"];
|
||||
type Size = VariantProps<typeof buttonVariants>["size"];
|
||||
|
||||
type Props = ButtonPrimitive.Props & {
|
||||
variant?: Variant;
|
||||
size?: Size;
|
||||
};
|
||||
|
||||
type Events = ButtonPrimitive.Events;
|
||||
|
||||
export {
|
||||
Root,
|
||||
type Props,
|
||||
type Events,
|
||||
//
|
||||
Root as Button,
|
||||
type Props as ButtonProps,
|
||||
type Events as ButtonEvents,
|
||||
buttonVariants,
|
||||
};
|
||||
28
front/src/lib/components/ui/table/index.ts
Normal file
28
front/src/lib/components/ui/table/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import Root from "./table.svelte";
|
||||
import Body from "./table-body.svelte";
|
||||
import Caption from "./table-caption.svelte";
|
||||
import Cell from "./table-cell.svelte";
|
||||
import Footer from "./table-footer.svelte";
|
||||
import Head from "./table-head.svelte";
|
||||
import Header from "./table-header.svelte";
|
||||
import Row from "./table-row.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
Body,
|
||||
Caption,
|
||||
Cell,
|
||||
Footer,
|
||||
Head,
|
||||
Header,
|
||||
Row,
|
||||
//
|
||||
Root as Table,
|
||||
Body as TableBody,
|
||||
Caption as TableCaption,
|
||||
Cell as TableCell,
|
||||
Footer as TableFooter,
|
||||
Head as TableHead,
|
||||
Header as TableHeader,
|
||||
Row as TableRow,
|
||||
};
|
||||
13
front/src/lib/components/ui/table/table-body.svelte
Normal file
13
front/src/lib/components/ui/table/table-body.svelte
Normal file
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLTableSectionElement>;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<tbody class={cn("[&_tr:last-child]:border-0", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</tbody>
|
||||
13
front/src/lib/components/ui/table/table-caption.svelte
Normal file
13
front/src/lib/components/ui/table/table-caption.svelte
Normal file
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLTableCaptionElement>;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<caption class={cn("mt-4 text-sm text-muted-foreground", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</caption>
|
||||
18
front/src/lib/components/ui/table/table-cell.svelte
Normal file
18
front/src/lib/components/ui/table/table-cell.svelte
Normal file
@@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLTdAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLTdAttributes;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<td
|
||||
class={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
>
|
||||
<slot />
|
||||
</td>
|
||||
13
front/src/lib/components/ui/table/table-footer.svelte
Normal file
13
front/src/lib/components/ui/table/table-footer.svelte
Normal file
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLTableSectionElement>;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<tfoot class={cn("bg-primary font-medium text-primary-foreground", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</tfoot>
|
||||
19
front/src/lib/components/ui/table/table-head.svelte
Normal file
19
front/src/lib/components/ui/table/table-head.svelte
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLThAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLThAttributes;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<th
|
||||
class={cn(
|
||||
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</th>
|
||||
14
front/src/lib/components/ui/table/table-header.svelte
Normal file
14
front/src/lib/components/ui/table/table-header.svelte
Normal file
@@ -0,0 +1,14 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLTableSectionElement>;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<thead class={cn("[&_tr]:border-b", className)} {...$$restProps} on:click on:keydown>
|
||||
<slot />
|
||||
</thead>
|
||||
23
front/src/lib/components/ui/table/table-row.svelte
Normal file
23
front/src/lib/components/ui/table/table-row.svelte
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLTableRowElement> & {
|
||||
"data-state"?: unknown;
|
||||
};
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<tr
|
||||
class={cn(
|
||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
>
|
||||
<slot />
|
||||
</tr>
|
||||
15
front/src/lib/components/ui/table/table.svelte
Normal file
15
front/src/lib/components/ui/table/table.svelte
Normal file
@@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLTableAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = HTMLTableAttributes;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<div class="relative w-full overflow-auto">
|
||||
<table class={cn("w-full caption-bottom text-sm", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</table>
|
||||
</div>
|
||||
1
front/src/lib/index.ts
Normal file
1
front/src/lib/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
62
front/src/lib/utils.ts
Normal file
62
front/src/lib/utils.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { cubicOut } from "svelte/easing";
|
||||
import type { TransitionConfig } from "svelte/transition";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
type FlyAndScaleParams = {
|
||||
y?: number;
|
||||
x?: number;
|
||||
start?: number;
|
||||
duration?: number;
|
||||
};
|
||||
|
||||
export const flyAndScale = (
|
||||
node: Element,
|
||||
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
|
||||
): TransitionConfig => {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === "none" ? "" : style.transform;
|
||||
|
||||
const scaleConversion = (
|
||||
valueA: number,
|
||||
scaleA: [number, number],
|
||||
scaleB: [number, number]
|
||||
) => {
|
||||
const [minA, maxA] = scaleA;
|
||||
const [minB, maxB] = scaleB;
|
||||
|
||||
const percentage = (valueA - minA) / (maxA - minA);
|
||||
const valueB = percentage * (maxB - minB) + minB;
|
||||
|
||||
return valueB;
|
||||
};
|
||||
|
||||
const styleToString = (
|
||||
style: Record<string, number | string | undefined>
|
||||
): string => {
|
||||
return Object.keys(style).reduce((str, key) => {
|
||||
if (style[key] === undefined) return str;
|
||||
return str + `${key}:${style[key]};`;
|
||||
}, "");
|
||||
};
|
||||
|
||||
return {
|
||||
duration: params.duration ?? 200,
|
||||
delay: 0,
|
||||
css: (t) => {
|
||||
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
|
||||
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
|
||||
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
|
||||
|
||||
return styleToString({
|
||||
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
|
||||
opacity: t
|
||||
});
|
||||
},
|
||||
easing: cubicOut
|
||||
};
|
||||
};
|
||||
7
front/src/routes/+layout.svelte
Normal file
7
front/src/routes/+layout.svelte
Normal file
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
</script>
|
||||
|
||||
<main class="container mt-10">
|
||||
<slot></slot>
|
||||
</main>
|
||||
30
front/src/routes/+page.server.ts
Normal file
30
front/src/routes/+page.server.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export async function load() {
|
||||
return (await fetch('http://localhost:3000').then((res) => res.json() as Promise<Data>)) as Data;
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
prepareDb: async () => {
|
||||
await fetch('http://localhost:3000/prepare-db');
|
||||
},
|
||||
|
||||
generateGroups: async () => {
|
||||
await fetch('http://localhost:3000/generate-groups');
|
||||
},
|
||||
|
||||
fillPreregistered: async () => {
|
||||
await fetch('http://localhost:3000/fill-preregistered');
|
||||
}
|
||||
};
|
||||
|
||||
export type UserData = {
|
||||
name: string;
|
||||
availableTime: { from: number; to: number | null };
|
||||
tgUsername: string;
|
||||
userId: string;
|
||||
chatId: string;
|
||||
};
|
||||
|
||||
export type Data = {
|
||||
rawData: Record<string, UserData>;
|
||||
groups: UserData[][];
|
||||
};
|
||||
80
front/src/routes/+page.svelte
Normal file
80
front/src/routes/+page.svelte
Normal file
@@ -0,0 +1,80 @@
|
||||
<script lang="ts">
|
||||
import type { Data } from './+page.server';
|
||||
|
||||
export let data: Data;
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableHeader
|
||||
} from '$lib/components/ui/table';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
</script>
|
||||
|
||||
<div class="w-full">
|
||||
<h1 class="mb-4 text-3xl font-bold">Группы</h1>
|
||||
<div class="mb-4 flex items-center gap-6">
|
||||
<form method="POST" action="?/prepareDb">
|
||||
<Button type="submit">Подготовить базу данных</Button>
|
||||
</form>
|
||||
<form method="POST" action="?/fillPreregistered">
|
||||
<Button type="submit">Добавить предрегистрированных пользователей</Button>
|
||||
</form>
|
||||
<form method="POST" action="?/generateGroups">
|
||||
<Button type="submit">Сгенерировать группы</Button>
|
||||
</form>
|
||||
<form>
|
||||
<Button>Отправить информацию в Telegram</Button>
|
||||
</form>
|
||||
</div>
|
||||
{#if Object.keys(data.rawData).length}
|
||||
<div class="mb-4">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Имя</TableHead>
|
||||
<TableHead>Время</TableHead>
|
||||
<TableHead>Telegram</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each Object.entries(data.rawData) as [_, user]}
|
||||
<TableRow>
|
||||
<TableCell>{user.name}</TableCell>
|
||||
<TableCell style="width: 200px"
|
||||
>{user.availableTime.from} - {user.availableTime.to}</TableCell
|
||||
>
|
||||
<TableCell style="width: 200px">{user.tgUsername}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="space-y-10">
|
||||
{#each data.groups as group}
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Имя</TableHead>
|
||||
<TableHead>Время</TableHead>
|
||||
<TableHead>Telegram</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each group as el}
|
||||
<TableRow>
|
||||
<TableCell>{el.name}</TableCell>
|
||||
<TableCell style="width: 200px"
|
||||
>{el.availableTime.from} - {el.availableTime.to}</TableCell
|
||||
>
|
||||
<TableCell style="width: 200px">{el.tgUsername}</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
BIN
front/static/favicon.png
Normal file
BIN
front/static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
18
front/svelte.config.js
Normal file
18
front/svelte.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
64
front/tailwind.config.ts
Normal file
64
front/tailwind.config.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { fontFamily } from "tailwindcss/defaultTheme";
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
const config: Config = {
|
||||
darkMode: ["class"],
|
||||
content: ["./src/**/*.{html,js,svelte,ts}"],
|
||||
safelist: ["dark"],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px"
|
||||
}
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
border: "hsl(var(--border) / <alpha-value>)",
|
||||
input: "hsl(var(--input) / <alpha-value>)",
|
||||
ring: "hsl(var(--ring) / <alpha-value>)",
|
||||
background: "hsl(var(--background) / <alpha-value>)",
|
||||
foreground: "hsl(var(--foreground) / <alpha-value>)",
|
||||
primary: {
|
||||
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
|
||||
foreground: "hsl(var(--primary-foreground) / <alpha-value>)"
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
|
||||
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)"
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
|
||||
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)"
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "hsl(var(--muted) / <alpha-value>)",
|
||||
foreground: "hsl(var(--muted-foreground) / <alpha-value>)"
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "hsl(var(--accent) / <alpha-value>)",
|
||||
foreground: "hsl(var(--accent-foreground) / <alpha-value>)"
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "hsl(var(--popover) / <alpha-value>)",
|
||||
foreground: "hsl(var(--popover-foreground) / <alpha-value>)"
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "hsl(var(--card) / <alpha-value>)",
|
||||
foreground: "hsl(var(--card-foreground) / <alpha-value>)"
|
||||
}
|
||||
},
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)"
|
||||
},
|
||||
fontFamily: {
|
||||
sans: [...fontFamily.sans]
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
19
front/tsconfig.json
Normal file
19
front/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
}
|
||||
6
front/vite.config.ts
Normal file
6
front/vite.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()]
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Stage, WizardScene } from 'telegraf/scenes';
|
||||
import { Data, UserData } from '../types.js';
|
||||
import fs from 'fs';
|
||||
import { UserData } from '../types.js';
|
||||
import * as db from '../db.js';
|
||||
|
||||
const superWizard = new WizardScene<any>(
|
||||
'add_to_team_wizard',
|
||||
@@ -135,9 +135,10 @@ const superWizard = new WizardScene<any>(
|
||||
...data,
|
||||
tgUsername: ctx.from?.username,
|
||||
chatId,
|
||||
userId: userId?.toString() ?? '',
|
||||
};
|
||||
|
||||
updateData({ userId: userId?.toString() ?? '', ...dataWithTgUsername });
|
||||
await updateData(dataWithTgUsername);
|
||||
await ctx.reply(
|
||||
`Спасибо за регистрацию! Мы свяжемся с тобой в ближайшее время.`,
|
||||
);
|
||||
@@ -152,10 +153,8 @@ const superWizard = new WizardScene<any>(
|
||||
|
||||
export const stage = new Stage([superWizard]);
|
||||
|
||||
function updateData({ ...rest }: UserData) {
|
||||
const data = fs.readFileSync('./data.json', 'utf8');
|
||||
const dataObj = JSON.parse(data) as Data;
|
||||
dataObj[rest.userId] = rest;
|
||||
const newDataStr = JSON.stringify(dataObj);
|
||||
fs.writeFileSync('./data.json', newDataStr, 'utf8');
|
||||
async function updateData({ ...rest }: UserData) {
|
||||
const data = await db.readData();
|
||||
data.rawData[rest.userId] = rest;
|
||||
await db.writeData(data);
|
||||
}
|
||||
|
||||
55
src/db.ts
Normal file
55
src/db.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { promises as fsp } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import path from 'node:path';
|
||||
|
||||
export type UserData = {
|
||||
name: string;
|
||||
availableTime: { from: number; to: number | null };
|
||||
tgUsername: string;
|
||||
userId: string;
|
||||
chatId: string;
|
||||
};
|
||||
|
||||
export type Data = {
|
||||
rawData: Record<string, UserData>;
|
||||
groups: UserData[][];
|
||||
};
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = path.join(__dirname, '../..');
|
||||
const dataDir = path.join(rootDir, 'data');
|
||||
const dataFilePath = path.join(dataDir, 'data.json');
|
||||
|
||||
const initialData: Data = {
|
||||
rawData: {},
|
||||
groups: [],
|
||||
};
|
||||
|
||||
async function readData(): Promise<Data> {
|
||||
try {
|
||||
const data = await fsp.readFile(dataFilePath, 'utf-8');
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
try {
|
||||
await writeData(initialData);
|
||||
return initialData;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function writeData(data: Data): Promise<void> {
|
||||
try {
|
||||
const jsonData = JSON.stringify(data, null, 2);
|
||||
await fsp.writeFile(dataFilePath, jsonData, 'utf-8');
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export { readData, writeData, initialData };
|
||||
@@ -6,7 +6,7 @@ generateGroups();
|
||||
function generateGroups() {
|
||||
const data = fs.readFileSync('./data.json', 'utf8');
|
||||
const dataObj = JSON.parse(data) as Data;
|
||||
const groupedUsers = groupByTime(dataObj);
|
||||
const groupedUsers = groupByTime(dataObj.rawData);
|
||||
fs.writeFileSync('./groups.json', JSON.stringify(groupedUsers), 'utf8');
|
||||
generateJsFile(groupedUsers);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ function generateJsFile(users: UserData[][]) {
|
||||
|
||||
fs.writeFileSync('./index.js', jsFile, 'utf8');
|
||||
}
|
||||
|
||||
function groupByTime(users: Record<string, UserData>): UserData[][] {
|
||||
const allUsers = Object.values(users);
|
||||
|
||||
|
||||
28
src/handlers/group-users.ts
Normal file
28
src/handlers/group-users.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { UserData } from '../types.js';
|
||||
|
||||
export function groupByTime(users: Record<string, UserData>): UserData[][] {
|
||||
const allUsers = Object.values(users);
|
||||
|
||||
// First, sort all users by their available time from low to high
|
||||
allUsers.sort((a, b) => a.availableTime.from - b.availableTime.from);
|
||||
|
||||
const groups: UserData[][] = [];
|
||||
let currentGroup: UserData[] = [];
|
||||
|
||||
allUsers.forEach((user) => {
|
||||
currentGroup.push(user);
|
||||
|
||||
// If the current group reaches 3 members, start a new group
|
||||
if (currentGroup.length === 3) {
|
||||
groups.push(currentGroup);
|
||||
currentGroup = []; // Reset for next group
|
||||
}
|
||||
});
|
||||
|
||||
// Add the last group if it has less than 3 members and is not empty
|
||||
if (currentGroup.length > 0) {
|
||||
groups.push(currentGroup);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
@@ -1,34 +1,87 @@
|
||||
// Import the framework and instantiate it
|
||||
import Fastify from 'fastify';
|
||||
import fs from 'fs';
|
||||
import { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'node:path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
import * as db from './db.js';
|
||||
import { promises as fsp } from 'node:fs';
|
||||
import { groupByTime } from './handlers/group-users.js';
|
||||
import dotenv from 'dotenv';
|
||||
import { session, Telegraf } from 'telegraf';
|
||||
import { stage } from './bot/setup-wizard-stage.js';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = path.join(__dirname, '../..');
|
||||
const dataDir = path.join(rootDir, 'data');
|
||||
const dataFilePath = path.join(dataDir, 'data.json');
|
||||
const preregisteredFilePath = path.join(dataDir, 'preregistered.json');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const BOT_TOKEN = process.env.BOT_TOKEN;
|
||||
|
||||
if (!BOT_TOKEN) {
|
||||
throw new Error('BOT_TOKEN is required!');
|
||||
}
|
||||
|
||||
const bot = new Telegraf(BOT_TOKEN);
|
||||
bot.use(session());
|
||||
bot.use(stage.middleware());
|
||||
bot.command('add_to_team', (ctx) => {
|
||||
// @ts-expect-error wtf
|
||||
ctx.scene.enter('add_to_team_wizard');
|
||||
});
|
||||
|
||||
const fastify = Fastify({
|
||||
logger: true,
|
||||
});
|
||||
|
||||
// Declare a route
|
||||
fastify.get('/', async function handler(request, reply) {
|
||||
return { hello: 'world' };
|
||||
return await db.readData();
|
||||
});
|
||||
|
||||
// Declare a route
|
||||
fastify.get('/prepare-db', async function handler(request, reply) {
|
||||
try {
|
||||
fs.writeFileSync(path.join(dataDir, 'data.json'), '{}', 'utf8');
|
||||
return reply.code(200).send('ok');
|
||||
await db.writeData(db.initialData);
|
||||
return reply.code(200).send();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return reply.code(500).send(err);
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/generate-groups', async function handler(request, reply) {
|
||||
try {
|
||||
const data = await db.readData();
|
||||
data.groups = groupByTime(data.rawData);
|
||||
await db.writeData(data);
|
||||
return reply.code(200).send();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return reply.code(500).send(err);
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/fill-preregistered', async function handler(request, reply) {
|
||||
try {
|
||||
const preregisteredData = await fsp.readFile(
|
||||
preregisteredFilePath,
|
||||
'utf-8',
|
||||
);
|
||||
const parsedPreregisteredData = JSON.parse(preregisteredData);
|
||||
await db.writeData(parsedPreregisteredData);
|
||||
return reply.code(200).send();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return reply.code(500).send(err);
|
||||
}
|
||||
});
|
||||
|
||||
void bot.launch();
|
||||
|
||||
process.once('SIGINT', () => bot.stop('SIGINT'));
|
||||
process.once('SIGTERM', () => bot.stop('SIGTERM'));
|
||||
|
||||
// Run the server!
|
||||
try {
|
||||
await fastify.listen({ port: 3000 });
|
||||
|
||||
@@ -6,4 +6,7 @@ export type UserData = {
|
||||
chatId: string;
|
||||
};
|
||||
|
||||
export type Data = Record<string, UserData>;
|
||||
export type Data = {
|
||||
rawData: Record<string, UserData>;
|
||||
groups: UserData[][];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user