Фронтенд вынесен в другой репозиторий
Этот коммит содержится в:
родитель
b0edb71ad4
Коммит
68581f6867
31197
frontend/package-lock.json
сгенерированный
31197
frontend/package-lock.json
сгенерированный
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "frontend",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@material/web": "^2.0.0",
|
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
|
||||||
"@testing-library/react": "^13.4.0",
|
|
||||||
"@testing-library/user-event": "^13.5.0",
|
|
||||||
"react": "^18.3.1",
|
|
||||||
"react-dom": "^18.3.1",
|
|
||||||
"react-scripts": "5.0.1",
|
|
||||||
"web-vitals": "^2.1.4"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "PORT=8088 react-scripts start",
|
|
||||||
"build": "react-scripts build",
|
|
||||||
"test": "react-scripts test",
|
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"react-app",
|
|
||||||
"react-app/jest"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
|
||||||
"production": [
|
|
||||||
">0.2%",
|
|
||||||
"not dead",
|
|
||||||
"not op_mini all"
|
|
||||||
],
|
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
:root {
|
|
||||||
--md-sys-color-primary: #D0BCFF;
|
|
||||||
--md-sys-color-on-primary: #381E72;
|
|
||||||
--md-sys-color-primary-container: #4F378B;
|
|
||||||
--md-sys-color-on-primary-container: #EADDFF;
|
|
||||||
--md-sys-color-secondary: #CCC2DC;
|
|
||||||
--md-sys-color-on-secondary: #332D41;
|
|
||||||
--md-sys-color-secondary-container: #4A4458;
|
|
||||||
--md-sys-color-on-secondary-container: #E8DEF8;
|
|
||||||
--md-sys-color-tertiary: #EFB8C8;
|
|
||||||
--md-sys-color-on-tertiary: #492532;
|
|
||||||
--md-sys-color-tertiary-container: #633B48;
|
|
||||||
--md-sys-color-on-tertiary-container: #FFD8E4;
|
|
||||||
--md-sys-color-error: #F2B8B5;
|
|
||||||
--md-sys-color-on-error: #601410;
|
|
||||||
--md-sys-color-error-container: #8C1D18;
|
|
||||||
--md-sys-color-on-error-container: #F9DEDC;
|
|
||||||
--md-sys-color-surface: #141218;
|
|
||||||
--md-sys-color-on-surface: #E6E0E9;
|
|
||||||
--md-sys-color-surface-variant: #49454F;
|
|
||||||
--md-sys-color-on-surface-variant: #CAC4D0;
|
|
||||||
--md-sys-color-surface-container-highest: #36343B;
|
|
||||||
--md-sys-color-surface-container-high: #2B2930;
|
|
||||||
--md-sys-color-surface-container: #211F26;
|
|
||||||
--md-sys-color-surface-container-low: #1D1B20;
|
|
||||||
--md-sys-color-surface-container-lowest: #0F0D13;
|
|
||||||
--md-sys-color-inverse-surface: #E6E0E9;
|
|
||||||
--md-sys-color-inverse-on-surface: #322F35;
|
|
||||||
--md-sys-color-surface-tint: #D0BCFF;
|
|
||||||
--md-sys-color-outline: #938F99;
|
|
||||||
--md-sys-color-outline-variant: #49454F;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#19a1e6">
|
|
||||||
<path d="M11.82,20.97c-4.77,0-8.64-3.87-8.64-8.64S7.05,3.69,11.82,3.69s8.64,3.87,8.64,8.64-3.87,8.64-8.64,8.64ZM11.19,5.52c-3.96,0-7.2,3.24-7.2,7.2s3.24,7.2,7.2,7.2,7.2-3.24,7.2-7.2-3.24-7.2-7.2-7.2Z" />
|
|
||||||
<circle cx="17.25" cy="6" r="6" />
|
|
||||||
<circle cx="4.95" cy="8.19" r="4.2" />
|
|
||||||
<circle cx="5.55" cy="17.7" r="3.3" />
|
|
||||||
<circle cx="16.05" cy="18.9" r="5.1" />
|
|
||||||
</svg>
|
|
До Ширина: | Высота: | Размер: 499 B |
@ -1,30 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0" />
|
|
||||||
<meta name="robots" content="none" />
|
|
||||||
<title>Настройки</title>
|
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.svg" type="image/svg+xml" />
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" type="text/css" />
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Symbols+Outlined" type="text/css" />
|
|
||||||
<script src="https://telegram.org/js/telegram-web-app.js" type="text/javascript"></script>
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
|
||||||
const link = document.createElement('link')
|
|
||||||
const href = new URL(location)
|
|
||||||
href.pathname = `%PUBLIC_URL%/${window.Telegram.WebApp.colorScheme}-theme.css`
|
|
||||||
link.rel = 'stylesheet'
|
|
||||||
link.href = href.toString()
|
|
||||||
link.type = 'text/css'
|
|
||||||
document.head.append(link)
|
|
||||||
|
|
||||||
window.Telegram.WebApp.expand()
|
|
||||||
window.Telegram.WebApp.ready()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,32 +0,0 @@
|
|||||||
:root {
|
|
||||||
--md-sys-color-primary: #6750A4;
|
|
||||||
--md-sys-color-on-primary: #FFFFFF;
|
|
||||||
--md-sys-color-primary-container: #EADDFF;
|
|
||||||
--md-sys-color-on-primary-container: #4F378B;
|
|
||||||
--md-sys-color-secondary: #625B71;
|
|
||||||
--md-sys-color-on-secondary: #FFFFFF;
|
|
||||||
--md-sys-color-secondary-container: #E8DEF8;
|
|
||||||
--md-sys-color-on-secondary-container: #4A4458;
|
|
||||||
--md-sys-color-tertiary: #7D5260;
|
|
||||||
--md-sys-color-on-tertiary: #FFFFFF;
|
|
||||||
--md-sys-color-tertiary-container: #FFD8E4;
|
|
||||||
--md-sys-color-on-tertiary-container: #633B48;
|
|
||||||
--md-sys-color-error: #B3261E;
|
|
||||||
--md-sys-color-on-error: #FFFFFF;
|
|
||||||
--md-sys-color-error-container: #F9DEDC;
|
|
||||||
--md-sys-color-on-error-container: #8C1D18;
|
|
||||||
--md-sys-color-surface: #FEF7FF;
|
|
||||||
--md-sys-color-on-surface: #1D1B20;
|
|
||||||
--md-sys-color-surface-variant: #E7E0EC;
|
|
||||||
--md-sys-color-on-surface-variant: #49454F;
|
|
||||||
--md-sys-color-surface-container-highest: #E6E0E9;
|
|
||||||
--md-sys-color-surface-container-high: #ECE6F0;
|
|
||||||
--md-sys-color-surface-container: #F3EDF7;
|
|
||||||
--md-sys-color-surface-container-low: #F7F2FA;
|
|
||||||
--md-sys-color-surface-container-lowest: #FFFFFF;
|
|
||||||
--md-sys-color-inverse-surface: #322F35;
|
|
||||||
--md-sys-color-inverse-on-surface: #F5EFF7;
|
|
||||||
--md-sys-color-surface-tint: #6750A4;
|
|
||||||
--md-sys-color-outline: #79747E;
|
|
||||||
--md-sys-color-outline-variant: #CAC4D0;
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Disallow: /
|
|
@ -1,3 +0,0 @@
|
|||||||
main > * {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
import React, { useEffect } from 'react'
|
|
||||||
import { styles as typescaleStyles } from '@material/web/typography/md-typescale-styles.js'
|
|
||||||
|
|
||||||
import PollsScreen from '../screens/PollsScreen'
|
|
||||||
import Scaffold from '../components/Scaffold'
|
|
||||||
import NavigationBar from '../components/NavigationBar'
|
|
||||||
|
|
||||||
import './MainActivity.css'
|
|
||||||
|
|
||||||
const bottomBar = (
|
|
||||||
<NavigationBar />
|
|
||||||
)
|
|
||||||
|
|
||||||
const floatingActionButton = (
|
|
||||||
<md-fab label="Создать опрос">
|
|
||||||
<md-icon slot="icon">add</md-icon>
|
|
||||||
</md-fab>
|
|
||||||
)
|
|
||||||
|
|
||||||
const content = (
|
|
||||||
<PollsScreen />
|
|
||||||
)
|
|
||||||
|
|
||||||
export default function MainActivity() {
|
|
||||||
useEffect(() => {
|
|
||||||
document.adoptedStyleSheets = [typescaleStyles.styleSheet]
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Scaffold
|
|
||||||
bottomBar={bottomBar}
|
|
||||||
floatingActionButton={floatingActionButton}
|
|
||||||
content={content} />
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
.card {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
background-color: var(--md-sys-color-surface-container);
|
|
||||||
border-radius: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card > * {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import './Card.css'
|
|
||||||
|
|
||||||
export default function Card({
|
|
||||||
children,
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className="card">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
.full-screen-dialog {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background-color: var(--md-sys-color-surface);
|
|
||||||
z-index: 2;
|
|
||||||
transition:
|
|
||||||
opacity .1s ease-in-out,
|
|
||||||
scale .1s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-screen-dialog.open {
|
|
||||||
opacity: 1;
|
|
||||||
scale: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-screen-dialog.close {
|
|
||||||
opacity: 0;
|
|
||||||
scale: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-screen-dialog > .scaffold > .content {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-screen-dialog > .scaffold > .content > * {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import '@material/web/fab/fab'
|
|
||||||
import '@material/web/icon/icon'
|
|
||||||
|
|
||||||
import Scaffold from './Scaffold'
|
|
||||||
import TopAppBar from './TopAppBar'
|
|
||||||
|
|
||||||
import './FullScreenDialog.css'
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
const floatingActionButton = (
|
|
||||||
<md-fab label="Добавить сообщение">
|
|
||||||
<md-icon slot="icon">add</md-icon>
|
|
||||||
</md-fab>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default function FullScreenDialog({
|
|
||||||
setEditPoll,
|
|
||||||
classList,
|
|
||||||
children,
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className={`full-screen-dialog ${classList}`}>
|
|
||||||
<Scaffold
|
|
||||||
topBar={
|
|
||||||
<TopAppBar onDismissRequest={() => setEditPoll(false)}/>
|
|
||||||
}
|
|
||||||
floatingActionButton={floatingActionButton}
|
|
||||||
content={children} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
nav {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding-bottom: .5rem;
|
|
||||||
background-color: var(--md-sys-color-surface-container-high);
|
|
||||||
}
|
|
||||||
|
|
||||||
nav ul {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
margin: 0;
|
|
||||||
padding: .5rem;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav li {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
color: var(--md-sys-color-on-surface);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav md-icon-button {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
import '@material/web/iconbutton/icon-button.js'
|
|
||||||
import '@material/web/icon/icon.js'
|
|
||||||
|
|
||||||
import './NavigationBar.css'
|
|
||||||
|
|
||||||
export default function NavigationBar() {
|
|
||||||
return (
|
|
||||||
<nav className="md-typescale-body-small">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<md-icon-button href="/">
|
|
||||||
<md-icon>dashboard</md-icon>
|
|
||||||
</md-icon-button>
|
|
||||||
Дашборд
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<md-icon-button href="/">
|
|
||||||
<md-icon>ballot</md-icon>
|
|
||||||
</md-icon-button>
|
|
||||||
Опросы
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<md-icon-button href="/">
|
|
||||||
<md-icon>chat</md-icon>
|
|
||||||
</md-icon-button>
|
|
||||||
Чаты
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<md-icon-button href="/">
|
|
||||||
<md-icon>group</md-icon>
|
|
||||||
</md-icon-button>
|
|
||||||
Пользователи
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
.poll-list-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-list-item .name {
|
|
||||||
color: var(--md-sys-color-on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-list-item .caption {
|
|
||||||
margin-top: .5rem;
|
|
||||||
color: var(--md-sys-color-on-surface-variant);
|
|
||||||
}
|
|
||||||
|
|
||||||
md-switch {
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
import '@material/web/list/list-item'
|
|
||||||
import '@material/web/ripple/ripple'
|
|
||||||
import '@material/web/switch/switch.js'
|
|
||||||
|
|
||||||
import './PollListItem.css'
|
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function PollListItem(
|
|
||||||
poll,
|
|
||||||
setEditPoll,
|
|
||||||
updatePoll,
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<md-list-item onClick={() => setEditPoll(poll)}>
|
|
||||||
<md-ripple></md-ripple>
|
|
||||||
<div className="poll-list-item">
|
|
||||||
<div>
|
|
||||||
<div className="name md-typescale-title-large">{poll.name}</div>
|
|
||||||
<div className="caption md-typescale-body-medium">
|
|
||||||
<span>{poll.daysOfWeek}</span>
|
|
||||||
•
|
|
||||||
<span>{poll.time}</span>
|
|
||||||
•
|
|
||||||
<span>{poll.questionNumber}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<md-switch onClickCapture={(event) => {
|
|
||||||
event.nativeEvent.stopPropagation()
|
|
||||||
poll.isEnabled = !poll.isEnabled
|
|
||||||
updatePoll(poll)
|
|
||||||
}} {...(poll.isEnabled ? {selected: true} : {})}></md-switch>
|
|
||||||
</div>
|
|
||||||
</md-list-item>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
.scaffold {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: var(--md-sys-color-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scaffold > .top-bar {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scaffold > .content {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
padding-bottom: 8rem !important;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scaffold > .bottom-bar {
|
|
||||||
flex-shrink: 0;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scaffold > .bottom-bar > aside {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 100%;
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scaffold > .bottom-bar > aside > * {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scaffold > .bottom-bar > aside > * > * {
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scaffold > .bottom-bar > aside > .floating-action-button {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import './Scaffold.css'
|
|
||||||
|
|
||||||
export default function Scaffold({
|
|
||||||
topBar,
|
|
||||||
bottomBar,
|
|
||||||
snackbarHost,
|
|
||||||
floatingActionButton,
|
|
||||||
content,
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className="scaffold">
|
|
||||||
<div className="top-bar">{topBar}</div>
|
|
||||||
<div className="content">
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
<div className="bottom-bar">
|
|
||||||
<aside>
|
|
||||||
<div className="snackbar">{snackbarHost}</div>
|
|
||||||
<div className="floating-action-button">{floatingActionButton}</div>
|
|
||||||
</aside>
|
|
||||||
{bottomBar}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
.top-app-bar {
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: .5rem;
|
|
||||||
padding: .5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-app-bar > * {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-app-bar > .title {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
color: var(--md-sys-color-on-surface);
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
import '@material/web/iconbutton/icon-button'
|
|
||||||
import '@material/web/icon/icon'
|
|
||||||
import '@material/web/button/filled-button'
|
|
||||||
|
|
||||||
import './TopAppBar.css'
|
|
||||||
|
|
||||||
export default function TopAppBar({
|
|
||||||
onDismissRequest,
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className="top-app-bar">
|
|
||||||
<md-icon-button onClick={() => onDismissRequest()}>
|
|
||||||
<md-icon>close</md-icon>
|
|
||||||
</md-icon-button>
|
|
||||||
<div className="title md-typescale-title-large">Изменить опрос</div>
|
|
||||||
<md-filled-button onClick={() => onDismissRequest()}>Сохранить</md-filled-button>
|
|
||||||
<md-icon-button>
|
|
||||||
<md-icon>more_vert</md-icon>
|
|
||||||
</md-icon-button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
*:not(input, textarea, label) {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
#root {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin: 0;
|
|
||||||
background-color: var(--md-sys-color-surface);
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import ReactDOM from 'react-dom/client'
|
|
||||||
import '@material/web/fab/fab'
|
|
||||||
|
|
||||||
import './index.css'
|
|
||||||
import MainActivity from "./activities/MainActivity";
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'))
|
|
||||||
|
|
||||||
root.render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<MainActivity />
|
|
||||||
</React.StrictMode>
|
|
||||||
)
|
|
@ -1,51 +0,0 @@
|
|||||||
.poll-edit .content label,
|
|
||||||
.poll-edit .content .messages-title,
|
|
||||||
.poll-edit .content .title {
|
|
||||||
color: var(--md-sys-color-on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content .messages-title {
|
|
||||||
margin: 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content .title:not(:first-child) {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content .poll-options {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content .poll-options > .poll-option {
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content .poll-options > .poll-option > .option-text {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content .poll-options > .poll-option > .option-action {
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content .message-settings {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
color: var(--md-sys-color-on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-edit .content .message-settings > * {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
@ -1,307 +0,0 @@
|
|||||||
import React, {useEffect, useState} from 'react'
|
|
||||||
import '@material/web/textfield/outlined-text-field'
|
|
||||||
import '@material/web/fab/fab'
|
|
||||||
import '@material/web/icon/icon'
|
|
||||||
import '@material/web/select/outlined-select'
|
|
||||||
import '@material/web/select/select-option'
|
|
||||||
import '@material/web/iconbutton/icon-button'
|
|
||||||
import '@material/web/checkbox/checkbox'
|
|
||||||
|
|
||||||
import PollListItem from '../components/PollListItem'
|
|
||||||
import FullScreenDialog from '../components/FullScreenDialog'
|
|
||||||
import Card from '../components/Card'
|
|
||||||
|
|
||||||
import './PollsScreen.css'
|
|
||||||
|
|
||||||
const PollContext = React.createContext({
|
|
||||||
polls: [],
|
|
||||||
fetchPolls: () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
const apiUrl = new URL(window.location)
|
|
||||||
apiUrl.pathname = '/api/polls'
|
|
||||||
|
|
||||||
const MessageType = {
|
|
||||||
TEXT: 'text',
|
|
||||||
POLL: 'poll',
|
|
||||||
MEDIA: 'media',
|
|
||||||
CONTACT: 'contact',
|
|
||||||
DICE: 'dice',
|
|
||||||
}
|
|
||||||
|
|
||||||
const getMessageTypeIcon = (messageType) => {
|
|
||||||
switch (messageType) {
|
|
||||||
case MessageType.TEXT: return 'title'
|
|
||||||
case MessageType.POLL: return 'ballot'
|
|
||||||
case MessageType.MEDIA: return 'attach_file'
|
|
||||||
case MessageType.CONTACT: return 'contacts'
|
|
||||||
case MessageType.DICE: return 'casino'
|
|
||||||
default: return 'chat'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderMessage (messageType) {
|
|
||||||
switch (messageType) {
|
|
||||||
case MessageType.TEXT:
|
|
||||||
return (
|
|
||||||
<div className="message-settings">
|
|
||||||
<div className="title md-typescale-title-small">Текст</div>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
type="textarea"
|
|
||||||
label="Текст"
|
|
||||||
value=""
|
|
||||||
rows="6"
|
|
||||||
required="required" />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
case MessageType.POLL:
|
|
||||||
return (
|
|
||||||
<div className="message-settings">
|
|
||||||
<div className="title md-typescale-title-small">Опрос</div>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
label="Вопрос"
|
|
||||||
value=""
|
|
||||||
required="required">
|
|
||||||
<md-icon slot="leading-icon">contact_support</md-icon>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<div className="title md-typescale-label-medium">Варианты ответа</div>
|
|
||||||
|
|
||||||
<div className="poll-options">
|
|
||||||
|
|
||||||
<div className="poll-option">
|
|
||||||
<md-outlined-text-field
|
|
||||||
class="option-text"
|
|
||||||
label="Добавить ответ"
|
|
||||||
value=""
|
|
||||||
required="required"/>
|
|
||||||
<md-icon-button class="option-action">
|
|
||||||
<md-icon>adjust</md-icon>
|
|
||||||
</md-icon-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="title md-typescale-title-small">Настройки</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="md-typescale-body-large">
|
|
||||||
<md-checkbox touch-target="wrapper"></md-checkbox>
|
|
||||||
Анонимное голосование
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className="md-typescale-body-large">
|
|
||||||
<md-checkbox touch-target="wrapper"></md-checkbox>
|
|
||||||
Выбор нескольких ответов
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
case MessageType.MEDIA:
|
|
||||||
return (
|
|
||||||
<div className="message-settings">
|
|
||||||
<div className="title md-typescale-title-small">Медиа</div>
|
|
||||||
|
|
||||||
<div className="messege-files"></div>
|
|
||||||
|
|
||||||
<md-filled-button>
|
|
||||||
Прикрепить файл
|
|
||||||
<md-icon slot="icon">attach_file</md-icon>
|
|
||||||
</md-filled-button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
case MessageType.CONTACT:
|
|
||||||
return (
|
|
||||||
<div className="message-settings">
|
|
||||||
<div className="title md-typescale-title-small">Контакт</div>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
label="Номер телефона"
|
|
||||||
value=""
|
|
||||||
required="required"/>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
label="Имя"
|
|
||||||
value=""
|
|
||||||
required="required"/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PollsScreen() {
|
|
||||||
const [polls, setPolls] = useState([])
|
|
||||||
const [editPoll, setEditPoll] = useState(false)
|
|
||||||
const [messageType, setMessageType] = useState(null)
|
|
||||||
|
|
||||||
const fetchPolls = async () => {
|
|
||||||
const response = await fetch('/api/polls')
|
|
||||||
const polls = await response.json()
|
|
||||||
setPolls(polls)
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatePoll = async (poll) => {
|
|
||||||
await fetch(apiUrl, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(poll)
|
|
||||||
})
|
|
||||||
await fetchPolls()
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchPolls()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section id="polls-screen">
|
|
||||||
|
|
||||||
<md-list>
|
|
||||||
<PollContext.Provider value={{polls, fetchPolls}}>
|
|
||||||
{polls.map((poll) => (
|
|
||||||
PollListItem(poll, setEditPoll, updatePoll)
|
|
||||||
))}
|
|
||||||
</PollContext.Provider>
|
|
||||||
</md-list>
|
|
||||||
|
|
||||||
<FullScreenDialog
|
|
||||||
classList={`poll-edit ${editPoll ? 'open' : 'close'}`}
|
|
||||||
setEditPoll={setEditPoll}>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
label="Название"
|
|
||||||
value={editPoll ? editPoll.name : ''}
|
|
||||||
supporting-text="Отображается в списке опросов"
|
|
||||||
required="required">
|
|
||||||
<md-icon slot="leading-icon">title</md-icon>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-select label="Дни недели" required="required">
|
|
||||||
|
|
||||||
<md-icon slot="leading-icon">date_range</md-icon>
|
|
||||||
|
|
||||||
<md-select-option value="mon">
|
|
||||||
<div slot="headline">Понедельник</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value="tue">
|
|
||||||
<div slot="headline">Вторник</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value="wed">
|
|
||||||
<div slot="headline">Среда</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value="thu">
|
|
||||||
<div slot="headline">Четверг</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value="fri">
|
|
||||||
<div slot="headline">Пятница</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value="sat">
|
|
||||||
<div slot="headline">Суббота</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value="sun">
|
|
||||||
<div slot="headline">Воскресенье</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
</md-outlined-select>
|
|
||||||
|
|
||||||
<section id="poll-messages">
|
|
||||||
|
|
||||||
<div className="messages-title md-typescale-title-medium">Сообщения</div>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
|
|
||||||
<div className="title md-typescale-title-small">Общее</div>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
label="Название"
|
|
||||||
value=""
|
|
||||||
supporting-text="Позволяет идентифицировать сообщение"
|
|
||||||
required="required">
|
|
||||||
<md-icon slot="leading-icon">title</md-icon>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
label="Время начала"
|
|
||||||
value=""
|
|
||||||
supporting-text="Время отправки сообщения"
|
|
||||||
required="required">
|
|
||||||
<md-icon slot="leading-icon">line_start_circle</md-icon>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<md-outlined-text-field
|
|
||||||
label="Время окончания"
|
|
||||||
value=""
|
|
||||||
supporting-text="Время прекращения обработки событий"
|
|
||||||
required="required">
|
|
||||||
<md-icon slot="leading-icon">line_end_circle</md-icon>
|
|
||||||
</md-outlined-text-field>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="md-typescale-body-large">
|
|
||||||
<md-checkbox touch-target="wrapper"></md-checkbox>
|
|
||||||
Не отправлять уведомление
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className="md-typescale-body-large">
|
|
||||||
<md-checkbox touch-target="wrapper"></md-checkbox>
|
|
||||||
Удалить после окончания
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<md-outlined-select
|
|
||||||
label="Тип"
|
|
||||||
value={messageType}
|
|
||||||
onInput={(event) => setMessageType(event.target.value)}
|
|
||||||
required="required">
|
|
||||||
|
|
||||||
<md-icon slot="leading-icon">{getMessageTypeIcon(messageType)}</md-icon>
|
|
||||||
|
|
||||||
<md-select-option value={MessageType.TEXT}>
|
|
||||||
<div slot="headline">Текст</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value={MessageType.POLL}>
|
|
||||||
<div slot="headline">Опрос</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value={MessageType.MEDIA}>
|
|
||||||
<div slot="headline">Медиа</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value={MessageType.CONTACT}>
|
|
||||||
<div slot="headline">Контакт</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
<md-select-option value={MessageType.DICE}>
|
|
||||||
<div slot="headline">Игральная кость</div>
|
|
||||||
</md-select-option>
|
|
||||||
|
|
||||||
</md-outlined-select>
|
|
||||||
|
|
||||||
{renderMessage(messageType)}
|
|
||||||
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</FullScreenDialog>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
Загрузка…
Ссылка в новой задаче
Block a user