Разработано несколько компонентов интерфейса веб-приложения
Этот коммит содержится в:
родитель
98bfb4196b
Коммит
7e7e3ef500
@ -19,6 +19,7 @@
|
|||||||
link.type = 'text/css'
|
link.type = 'text/css'
|
||||||
document.head.append(link)
|
document.head.append(link)
|
||||||
|
|
||||||
|
window.Telegram.WebApp.expand()
|
||||||
window.Telegram.WebApp.ready()
|
window.Telegram.WebApp.ready()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -12,10 +12,6 @@
|
|||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
main > * {
|
main > * {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react'
|
||||||
|
import { styles as typescaleStyles } from '@material/web/typography/md-typescale-styles.js'
|
||||||
|
|
||||||
import { styles as typescaleStyles } from '@material/web/typography/md-typescale-styles.js';
|
import PollList from "./PollList";
|
||||||
import '@material/web/switch/switch.js';
|
|
||||||
|
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
|
||||||
@ -9,10 +9,11 @@ function App() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.adoptedStyleSheets = [typescaleStyles.styleSheet]
|
document.adoptedStyleSheets = [typescaleStyles.styleSheet]
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="App">
|
<main className="App">
|
||||||
<md-outlined-text-field id="text" label="Favorite color"></md-outlined-text-field>
|
<md-outlined-text-field id="text" label="Favorite color"></md-outlined-text-field>
|
||||||
<md-switch id="switch"></md-switch>
|
<PollList />
|
||||||
</main>
|
</main>
|
||||||
// <div className="App">
|
// <div className="App">
|
||||||
// <header className="App-header">
|
// <header className="App-header">
|
||||||
|
45
frontend/src/FullScreenDialog.css
Обычный файл
45
frontend/src/FullScreenDialog.css
Обычный файл
@ -0,0 +1,45 @@
|
|||||||
|
.full-screen-dialog {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--md-sys-color-surface-container);
|
||||||
|
z-index: 1;
|
||||||
|
animation: .1s show ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-screen-dialog > .top-app-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .5rem;
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-screen-dialog > .top-app-bar > * {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-screen-dialog > .top-app-bar > .title {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes show {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
scale: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
scale: 1;
|
||||||
|
}
|
||||||
|
}
|
28
frontend/src/FullScreenDialog.js
Обычный файл
28
frontend/src/FullScreenDialog.js
Обычный файл
@ -0,0 +1,28 @@
|
|||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import '@material/web/iconbutton/icon-button.js'
|
||||||
|
import '@material/web/icon/icon.js'
|
||||||
|
import '@material/web/button/filled-button'
|
||||||
|
|
||||||
|
import './FullScreenDialog.css'
|
||||||
|
|
||||||
|
export default function ({
|
||||||
|
editPoll,
|
||||||
|
setEditPoll,
|
||||||
|
}) {
|
||||||
|
// const [close, onClose] = useState(false)
|
||||||
|
return (
|
||||||
|
<div className="full-screen-dialog">
|
||||||
|
<div className="top-app-bar">
|
||||||
|
<md-icon-button onClick={() => setEditPoll(false)}>
|
||||||
|
<md-icon>close</md-icon>
|
||||||
|
</md-icon-button>
|
||||||
|
<div className="title md-typescale-title-large">Изменить опрос</div>
|
||||||
|
<md-filled-button onClick={() => setEditPoll()}>Сохранить</md-filled-button>
|
||||||
|
<md-icon-button>
|
||||||
|
<md-icon>more_vert</md-icon>
|
||||||
|
</md-icon-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
4
frontend/src/PollList.css
Обычный файл
4
frontend/src/PollList.css
Обычный файл
@ -0,0 +1,4 @@
|
|||||||
|
.poll-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
37
frontend/src/PollList.js
Обычный файл
37
frontend/src/PollList.js
Обычный файл
@ -0,0 +1,37 @@
|
|||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import PollListItem from "./PollListItem"
|
||||||
|
import FullScreenDialog from "./FullScreenDialog"
|
||||||
|
|
||||||
|
import './PollList.css'
|
||||||
|
|
||||||
|
const PollContext = React.createContext({
|
||||||
|
polls: [],
|
||||||
|
fetchPolls: () => {},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function PollList() {
|
||||||
|
const [polls, setPolls] = useState([])
|
||||||
|
const [editPoll, setEditPoll] = useState(false)
|
||||||
|
|
||||||
|
const fetchPolls = async () => {
|
||||||
|
const response = await fetch('/api/polls')
|
||||||
|
const polls = await response.json()
|
||||||
|
setPolls(polls)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchPolls()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="poll-list">
|
||||||
|
<PollContext.Provider value={{polls, fetchPolls}}>
|
||||||
|
{polls.map((poll) => (
|
||||||
|
PollListItem(poll, setEditPoll)
|
||||||
|
))}
|
||||||
|
</PollContext.Provider>
|
||||||
|
{editPoll ? <FullScreenDialog editPoll={editPoll} setEditPoll={setEditPoll} /> : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
20
frontend/src/PollListItem.css
Обычный файл
20
frontend/src/PollListItem.css
Обычный файл
@ -0,0 +1,20 @@
|
|||||||
|
.poll-list-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
22
frontend/src/PollListItem.js
Обычный файл
22
frontend/src/PollListItem.js
Обычный файл
@ -0,0 +1,22 @@
|
|||||||
|
import '@material/web/switch/switch.js'
|
||||||
|
|
||||||
|
import './PollListItem.css'
|
||||||
|
|
||||||
|
export default function PollListItem(data, setEditPoll) {
|
||||||
|
return (
|
||||||
|
<div className="poll-list-item" onClick={() => setEditPoll(true)}>
|
||||||
|
<md-ripple></md-ripple>
|
||||||
|
<div>
|
||||||
|
<div className="name md-typescale-title-large">{data.name}</div>
|
||||||
|
<div className="caption md-typescale-body-medium">
|
||||||
|
<span>{data.days_of_week}</span>
|
||||||
|
•
|
||||||
|
<span>{data.time}</span>
|
||||||
|
•
|
||||||
|
<span>{data.question_number}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<md-switch></md-switch>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,3 +1,7 @@
|
|||||||
|
*:not(input, textarea, label) {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client'
|
||||||
import './index.css';
|
import './index.css'
|
||||||
import App from './App';
|
import App from './App'
|
||||||
import BottomNavBar from "./BottomNavBar";
|
import BottomNavBar from "./BottomNavBar"
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById('root'))
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
<BottomNavBar />
|
<BottomNavBar />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
)
|
||||||
|
52
web/main.py
52
web/main.py
@ -72,15 +72,49 @@ async def _():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.websocket(
|
polls = [
|
||||||
path='/ws/sync',
|
{
|
||||||
|
'id': 1,
|
||||||
|
'name': 'Текущее состояние сотрудников',
|
||||||
|
'days_of_week': 'ПН-ПТ',
|
||||||
|
'time': '11:00',
|
||||||
|
'question_number': '1 вопрос',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 2,
|
||||||
|
'name': 'Планы на обед',
|
||||||
|
'days_of_week': 'ПН-ПТ',
|
||||||
|
'time': '11:45-12:00',
|
||||||
|
'question_number': '2 вопроса',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get(
|
||||||
|
path='/api/polls',
|
||||||
|
)
|
||||||
|
async def _():
|
||||||
|
return polls
|
||||||
|
|
||||||
|
|
||||||
|
@app.post(
|
||||||
|
path='/api/polls',
|
||||||
)
|
)
|
||||||
async def _(
|
async def _(
|
||||||
websocket: WebSocket,
|
poll: dict,
|
||||||
):
|
):
|
||||||
await connection_manager.connect(websocket)
|
polls.append(poll)
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
await connection_manager.broadcast(await websocket.receive_json())
|
# @app.websocket(
|
||||||
except WebSocketDisconnect:
|
# path='/ws/sync',
|
||||||
connection_manager.disconnect(websocket)
|
# )
|
||||||
|
# async def _(
|
||||||
|
# websocket: WebSocket,
|
||||||
|
# ):
|
||||||
|
# await connection_manager.connect(websocket)
|
||||||
|
# try:
|
||||||
|
# while True:
|
||||||
|
# await connection_manager.broadcast(await websocket.receive_json())
|
||||||
|
# except WebSocketDisconnect:
|
||||||
|
# connection_manager.disconnect(websocket)
|
||||||
|
Загрузка…
Ссылка в новой задаче
Block a user