Compare commits
No commits in common. "7a044970f02d911a0addffa6d8c5a78f1ef3e411" and "de8a1abcbfea61d8d4898c18e133b8b0feaf87e8" have entirely different histories.
7a044970f0
...
de8a1abcbf
17
front/package-lock.json
generated
17
front/package-lock.json
generated
@ -20,7 +20,6 @@
|
||||
"react-router-dom": "^6.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
||||
@ -818,22 +817,6 @@
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@faker-js/faker": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.0.2.tgz",
|
||||
"integrity": "sha512-Uo3pGspElQW91PCvKSIAXoEgAUlRnH29sX2/p89kg7sP1m2PzCufHINd0FhTXQf6DYGiUlVncdSPa2F9wxed2A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fakerjs"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
"npm": ">=6.14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
|
||||
|
@ -24,7 +24,6 @@
|
||||
"react-router-dom": "^6.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
||||
|
@ -1,315 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav>
|
||||
<a href="#back" class="back">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z" />
|
||||
</svg>
|
||||
</a>
|
||||
<h1 class="heading">Иванов Иван, с нами с 17.07.2023</h1>
|
||||
</nav>
|
||||
<div id="root"></div>
|
||||
<div class="poemContainer">
|
||||
<h1>Поэзия</h1>
|
||||
<div class="poemText">
|
||||
<div class="eleven" style="position:relative;left:-60px">"Fury said to</div>
|
||||
<div class="ten" style="position:relative;left:-40px">a mouse, That</div>
|
||||
<div class="ten" style="position:relative;left:0px">he met</div>
|
||||
<div class="ten" style="position:relative;left:10px">in the</div>
|
||||
<div class="ten" style="position:relative;left:20px">house,</div>
|
||||
<div class="ten" style="position:relative;left:17px">'Let us</div>
|
||||
<div class="ten" style="position:relative;left:5px">both go</div>
|
||||
<div class="ten" style="position:relative;left:-7px">to law:</div>
|
||||
<div class="ten" style="position:relative;left:-23px"><i>I</i> will</div>
|
||||
<div class="ten" style="position:relative;left:-26px">prosecute</div>
|
||||
<div class="nine" style="position:relative;left:-40px"><i>you.</i>—</div>
|
||||
<div class="nine" style="position:relative;left:-30px">Come, I'll</div>
|
||||
<div class="nine" style="position:relative;left:-20px">take no</div>
|
||||
<div class="nine" style="position:relative;left:-7px">denial;</div>
|
||||
<div class="nine" style="position:relative;left:19px">We must</div>
|
||||
<div class="nine" style="position:relative;left:45px">have a</div>
|
||||
<div class="nine" style="position:relative;left:67px">trial:</div>
|
||||
<div class="nine" style="position:relative;left:80px">For</div>
|
||||
<div class="eight" style="position:relative;left:70px">really</div>
|
||||
<div class="eight" style="position:relative;left:57px">this</div>
|
||||
<div class="eight" style="position:relative;left:75px">morning</div>
|
||||
<div class="eight" style="position:relative;left:95px">I've</div>
|
||||
<div class="eight" style="position:relative;left:77px">nothing</div>
|
||||
<div class="eight" style="position:relative;left:57px">to do.'</div>
|
||||
<div class="seven" style="position:relative;left:38px">Said the</div>
|
||||
<div class="seven" style="position:relative;left:30px">mouse to</div>
|
||||
<div class="seven" style="position:relative;left:18px">the cur,</div>
|
||||
<div class="seven" style="position:relative;left:22px">'Such a</div>
|
||||
<div class="seven" style="position:relative;left:37px">trial,</div>
|
||||
<div class="seven" style="position:relative;left:27px">dear sir,</div>
|
||||
<div class="seven" style="position:relative;left:9px">With no</div>
|
||||
<div class="seven" style="position:relative;left:-8px">jury or</div>
|
||||
<div class="seven" style="position:relative;left:-18px">judge,</div>
|
||||
<div class="seven" style="position:relative;left:-6px">would be</div>
|
||||
<div class="seven" style="position:relative;left:7px">wasting</div>
|
||||
<div class="seven" style="position:relative;left:25px">our breath.'</div>
|
||||
<div class="six" style="position:relative;left:30px">'I'll be</div>
|
||||
<div class="six" style="position:relative;left:24px">judge,</div>
|
||||
<div class="six" style="position:relative;left:15px">I'll be</div>
|
||||
<div class="six" style="position:relative;left:2px">jury,'</div>
|
||||
<div class="six" style="position:relative;left:-4px">Said</div>
|
||||
<div class="six" style="position:relative;left:17px">cunning</div>
|
||||
<div class="six" style="position:relative;left:29px">old Fury;</div>
|
||||
<div class="six" style="position:relative;left:37px">'I'll try</div>
|
||||
<div class="six" style="position:relative;left:51px">the whole</div>
|
||||
<div class="six" style="position:relative;left:70px">cause,</div>
|
||||
<div class="six" style="position:relative;left:65px">and</div>
|
||||
<div class="six" style="position:relative;left:60px">condemn</div>
|
||||
<div class="six" style="position:relative;left:60px">you</div>
|
||||
<div class="six" style="position:relative;left:68px">to</div>
|
||||
<div class="six" style="position:relative;left:82px">death.' "</div>
|
||||
</div>
|
||||
<img src="uploads/mouse.jpg" class="poemImg">
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
const categoryGraphics = {
|
||||
'PORRIDGE': 'dist/PORRIDGE.jpg',
|
||||
'conspects': 'dist/conspects.jpg',
|
||||
'milk': 'dist/milk.jpg',
|
||||
'bred': 'dist/bred.jpg',
|
||||
'wathing': 'dist/wathing.jpg',
|
||||
'cloth': 'dist/cloth.jpg',
|
||||
'fruits_vegatables': 'dist/fruits_vegatables.jpg',
|
||||
'soup': 'dist/soup.jpg',
|
||||
'dinner': 'dist/dinner.jpg',
|
||||
'conserves': 'dist/conserves.jpg',
|
||||
'pens': 'dist/pens.jpg',
|
||||
'other_things': 'dist/other_things.jpg',
|
||||
}
|
||||
|
||||
const categoryNames = {
|
||||
'PORRIDGE': 'PORRIDGE',
|
||||
'conspects': 'Конспекты',
|
||||
'milk': 'Молочные продукты',
|
||||
'bred': 'Хлебобулочные изделия',
|
||||
'wathing': 'Моющие средства',
|
||||
'cloth': 'Одежда',
|
||||
'fruits_vegatables': 'Фрукты и овощи',
|
||||
'soup': 'Супы',
|
||||
'dinner': 'Ужин',
|
||||
'conserves': 'Консервы',
|
||||
'pens': 'Канцелярия',
|
||||
'other_things': 'Всякая всячина',
|
||||
}
|
||||
|
||||
const cats = ["Раздача", "Бронь", "История"]
|
||||
|
||||
const props = ["Годен до 01.09.2023", "Бронь ещё 5 чел.", "Забрал 16.07.2023"]
|
||||
|
||||
|
||||
const stories = [2, 4, 1].map(
|
||||
(n) => (new Array(n)).fill(1).map(
|
||||
() => (
|
||||
{
|
||||
title: (Math.random() * Math.pow(10, Math.random() * 100)).toString(36),
|
||||
category: Object.keys(categoryGraphics)[Math.round(Math.random() * (Object.keys(categoryGraphics).length - 1))]
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
console.log(stories)
|
||||
|
||||
const render = () => {
|
||||
const root = document.getElementById('root')
|
||||
|
||||
root.innerHTML = ''
|
||||
|
||||
stories.forEach((c, i) => {
|
||||
const section = document.createElement('section')
|
||||
section.className = 'section'
|
||||
section.innerHTML = `<h1>${cats[i]}</h1>`
|
||||
const ul = document.createElement('ul')
|
||||
c.forEach((v, j) => {
|
||||
const story = document.createElement('li')
|
||||
story.className = 'story'
|
||||
story.innerHTML = `
|
||||
<a href="#${i},${j}">
|
||||
<img class="storyPic" src="${categoryGraphics[v.category]}" />
|
||||
<p class="storyTitle">${v.title}</p>
|
||||
<p class="storyTitle">${props[i]}</p>
|
||||
`.trim()
|
||||
ul.appendChild(story)
|
||||
})
|
||||
|
||||
console.log(window.innerWidth, (window.innerHeight * 0.25 * 9 / 16), (window.innerWidth * 0.25 * 9 / 16) * c.length)
|
||||
|
||||
if ((window.innerWidth - 60) < (window.innerHeight * 0.25 * 9 / 16 + 10) * c.length) {
|
||||
const seeMore = document.createElement('a')
|
||||
seeMore.href = "#more"
|
||||
seeMore.innerHTML = `<svg fill="currentColor" width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><!--! Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M64 448c-8.188 0-16.38-3.125-22.62-9.375c-12.5-12.5-12.5-32.75 0-45.25L178.8 256L41.38 118.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0l160 160c12.5 12.5 12.5 32.75 0 45.25l-160 160C80.38 444.9 72.19 448 64 448z"/></svg>`
|
||||
seeMore.className = 'seeMore'
|
||||
|
||||
ul.appendChild(seeMore)
|
||||
ul.classList.add('grad')
|
||||
}
|
||||
|
||||
section.appendChild(ul)
|
||||
root.appendChild(section)
|
||||
})
|
||||
}
|
||||
|
||||
// window.addEventListener('resize', render)
|
||||
document.addEventListener('DOMContentLoaded', render)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgb(185, 179, 170);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #111;
|
||||
color: rgb(185, 179, 170);
|
||||
width: 100%;
|
||||
max-width: calc(100vh*9/16);
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 30px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
list-style-type: none;
|
||||
padding-top: 20px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grad::after {
|
||||
content: '';
|
||||
background: linear-gradient(to right, rgba(17, 17, 17, 0) 0%, rgba(17, 17, 17, 255) 100%);
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 10%;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding-right: 10px;
|
||||
width: calc(25vh*9/16);
|
||||
}
|
||||
|
||||
.storyPic {
|
||||
max-height: 25vh;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.storyTitle {
|
||||
padding-top: 5px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
/* max-width: 100%; */
|
||||
}
|
||||
|
||||
.seeMore {
|
||||
position: absolute;
|
||||
left: calc(100% - 5% / 3 - 30px + 8px);
|
||||
top: calc(50% - 15px);
|
||||
z-index: 100;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 3px;
|
||||
color: rgb(185, 179, 170);
|
||||
/* background-color: #111; */
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 30px;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.back {
|
||||
color: rgb(185, 179, 170);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.back svg {
|
||||
display: block;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
.poemContainer {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.poemText {
|
||||
padding: 0 60px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.eleven {
|
||||
font-size: 105%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.ten {
|
||||
font-size: 100%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.nine {
|
||||
font-size: 90%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.eight {
|
||||
font-size: 80%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.seven {
|
||||
font-size: 70%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.six {
|
||||
font-size: 60%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.poemImg {
|
||||
max-width: 100%;
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,5 +1,12 @@
|
||||
:root {
|
||||
--bs-body-bg: rgb(17, 17, 17) !important;
|
||||
body {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.modal-content, .modal-content .form-select {
|
||||
background-color: rgb(17, 17, 17) !important;
|
||||
}
|
||||
|
||||
/* В связи со сложившейся политической обстановкой */
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { Announcement, AnnouncementResponse } from './types'
|
||||
|
||||
const processAnnouncement = (data: AnnouncementResponse): Announcement => ({
|
||||
...data,
|
||||
lat: data.latitude,
|
||||
lng: data.longtitude,
|
||||
bestBy: data.best_by,
|
||||
bookedBy: data.booked_by,
|
||||
userId: data.user_id
|
||||
})
|
||||
|
||||
export { processAnnouncement }
|
@ -1,6 +1,5 @@
|
||||
import { API_URL } from '../../config'
|
||||
import { FiltersType, URLEncodeFilters } from '../../utils/filters'
|
||||
import { processAnnouncement } from '../announcement'
|
||||
import { Announcement } from '../announcement/types'
|
||||
import { AnnouncementsResponse } from './types'
|
||||
|
||||
@ -10,8 +9,17 @@ const composeAnnouncementsURL = (filters: FiltersType) => (
|
||||
API_URL + '/announcements?' + new URLSearchParams(URLEncodeFilters(filters)).toString()
|
||||
)
|
||||
|
||||
const processAnnouncements = (data: AnnouncementsResponse): Announcement[] => (
|
||||
data.list_of_announcements.map(processAnnouncement)
|
||||
)
|
||||
const processAnnouncements = (data: AnnouncementsResponse): Announcement[] => {
|
||||
const annList = data.list_of_announcements
|
||||
|
||||
return annList.map(ann => ({
|
||||
...ann,
|
||||
lat: ann.latitude,
|
||||
lng: ann.longtitude,
|
||||
bestBy: ann.best_by,
|
||||
bookedBy: ann.booked_by,
|
||||
userId: ann.user_id
|
||||
}))
|
||||
}
|
||||
|
||||
export { initialAnnouncements, composeAnnouncementsURL, processAnnouncements }
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { API_URL } from '../../config'
|
||||
import { UserResponse, User } from './types'
|
||||
|
||||
import { faker } from '@faker-js/faker/locale/ru'
|
||||
|
||||
|
||||
const initialUser: User = import.meta.env.DEV ? { // Temporary, until api is realized
|
||||
id: Math.random() * 100,
|
||||
name: faker.person.firstName() + ' ' + faker.person.lastName(),
|
||||
regDate: faker.date.anytime().getTime(),
|
||||
} : {
|
||||
id: -1,
|
||||
name: '',
|
||||
regDate: 0,
|
||||
}
|
||||
|
||||
const composeUserURL = () => (
|
||||
API_URL + '/user?'
|
||||
)
|
||||
|
||||
const processUser = (data: UserResponse): User => {
|
||||
return data
|
||||
}
|
||||
|
||||
export { initialUser, composeUserURL, processUser }
|
@ -1,29 +0,0 @@
|
||||
import { isObject } from '../../utils/types'
|
||||
|
||||
type User = {
|
||||
id: number,
|
||||
name: string,
|
||||
regDate: number,
|
||||
}
|
||||
|
||||
const isUser = (obj: unknown): obj is User => (
|
||||
isObject(obj, {
|
||||
'id': 'number',
|
||||
'name': 'string',
|
||||
'regDate': 'number',
|
||||
})
|
||||
)
|
||||
|
||||
type UserResponse = User
|
||||
|
||||
// const isUserResponse = (obj: unknown): obj is UserResponse => (
|
||||
// isObject(obj, {
|
||||
|
||||
// })
|
||||
// )
|
||||
|
||||
const isUserResponse = isUser
|
||||
|
||||
export type { UserResponse, User }
|
||||
|
||||
export { isUserResponse, isUser }
|
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(185, 179, 170)" width="24" height="24" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd"
|
||||
d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z" />
|
||||
</svg>
|
Before Width: | Height: | Size: 329 B |
@ -1,4 +0,0 @@
|
||||
<svg fill="rgb(185, 179, 170)" width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512">
|
||||
<!--! Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. -->
|
||||
<path d="M64 448c-8.188 0-16.38-3.125-22.62-9.375c-12.5-12.5-12.5-32.75 0-45.25L178.8 256L41.38 118.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0l160 160c12.5 12.5 12.5 32.75 0 45.25l-160 160C80.38 444.9 72.19 448 64 448z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 569 B |
@ -1,45 +0,0 @@
|
||||
import { Announcement } from '../api/announcement/types'
|
||||
import { FiltersType } from '../utils/filters'
|
||||
|
||||
const userCategories = ['givingOut', 'booked', 'history'] as const
|
||||
|
||||
type UserCategory = typeof userCategories[number]
|
||||
|
||||
const UserCategoriesNames: Record<UserCategory, string> = {
|
||||
givingOut: 'Раздача',
|
||||
booked: 'Бронь',
|
||||
history: 'История',
|
||||
}
|
||||
|
||||
const userCategoriesInfos: Record<UserCategory, (ann: Announcement) => string> = {
|
||||
givingOut: (ann: Announcement) => (
|
||||
`Годен до ${new Date(ann.bestBy).toLocaleDateString('ru')}`
|
||||
),
|
||||
booked: (ann: Announcement) => (
|
||||
`Бронь ещё ${(ann as Announcement & { bookedBy: number[] }).bookedBy.length} чел.`
|
||||
),
|
||||
history: (ann: Announcement) => (
|
||||
`Забрал ${new Date((ann as Announcement & { taken: number }).taken).toLocaleDateString('ru')}`
|
||||
),
|
||||
}
|
||||
|
||||
const composeUserCategoriesFilters: Record<UserCategory, () => FiltersType> = {
|
||||
givingOut: () => {
|
||||
const userId = -1
|
||||
|
||||
return ({ userId })
|
||||
},
|
||||
booked: () => {
|
||||
const userId = -1
|
||||
|
||||
return ({ bookedBy: userId })
|
||||
},
|
||||
history: () => {
|
||||
const userId = -1
|
||||
|
||||
return ({ userId, status: 'taken' })
|
||||
}
|
||||
}
|
||||
|
||||
export type { UserCategory }
|
||||
export { userCategories, UserCategoriesNames, userCategoriesInfos, composeUserCategoriesFilters }
|
@ -1,25 +0,0 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Navbar } from 'react-bootstrap'
|
||||
|
||||
import BackButton from '../assets/backArrow.svg'
|
||||
|
||||
type BackHeaderProps = {
|
||||
text: string
|
||||
}
|
||||
|
||||
function BackHeader({ text }: BackHeaderProps) {
|
||||
return (
|
||||
<Navbar>
|
||||
<Navbar.Brand as={Link} to='/'>
|
||||
<img src={BackButton} alt='Go back' />
|
||||
</Navbar.Brand>
|
||||
<Navbar.Text className='me-auto'>
|
||||
<h4 className='mb-0'>
|
||||
{text}
|
||||
</h4>
|
||||
</Navbar.Text>
|
||||
</Navbar>
|
||||
)
|
||||
}
|
||||
|
||||
export default BackHeader
|
@ -1,25 +0,0 @@
|
||||
import { StoriesPreview } from '.'
|
||||
import { UserCategoriesNames, UserCategory, composeUserCategoriesFilters } from '../assets/userCategories'
|
||||
import { useAnnouncements } from '../hooks/api'
|
||||
import { gotError } from '../hooks/useFetch'
|
||||
|
||||
type CategoryPreviewProps = {
|
||||
category: UserCategory
|
||||
}
|
||||
|
||||
function CategoryPreview({ category }: CategoryPreviewProps) {
|
||||
const announcements = useAnnouncements(composeUserCategoriesFilters[category]())
|
||||
|
||||
return (
|
||||
<section>
|
||||
<h4 className='fw-bold'>{UserCategoriesNames[category]}</h4>
|
||||
{gotError(announcements) ? (
|
||||
<p className='text-danger'>{announcements.error}</p>
|
||||
) : (announcements.loading ? 'Загрузка...' :
|
||||
<StoriesPreview announcements={announcements.data} category={category} />
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default CategoryPreview
|
@ -1,143 +0,0 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { CSSProperties, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { UserCategory, userCategoriesInfos } from '../assets/userCategories'
|
||||
import { Announcement } from '../api/announcement/types'
|
||||
import { categoryGraphics, categoryNames } from '../assets/category'
|
||||
|
||||
import rightAngleIcon from '../assets/rightAngle.svg'
|
||||
import { Button } from 'react-bootstrap'
|
||||
|
||||
type StoriesPreviewProps = {
|
||||
announcements: Announcement[],
|
||||
category: UserCategory,
|
||||
}
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
transform: 'translateX(0)',
|
||||
} as CSSProperties,
|
||||
ul: {
|
||||
display: 'flex',
|
||||
gap: 10,
|
||||
listStyleType: 'none',
|
||||
overflow: 'scroll',
|
||||
paddingLeft: 0,
|
||||
scrollBehavior: 'smooth',
|
||||
} as CSSProperties,
|
||||
link: {
|
||||
textDecoration: 'none',
|
||||
color: 'var(--bs-body-color)'
|
||||
} as CSSProperties,
|
||||
image: {
|
||||
height: '25vh',
|
||||
maxWidth: 'calc(25vh * 9 / 16)',
|
||||
objectFit: 'contain',
|
||||
borderRadius: 12,
|
||||
marginBottom: 5,
|
||||
} as CSSProperties,
|
||||
title: {
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
marginBottom: 5,
|
||||
} as CSSProperties,
|
||||
scrollButton: {
|
||||
position: 'fixed',
|
||||
right: 0,
|
||||
top: 0,
|
||||
zIndex: 100,
|
||||
background: 'linear-gradient(to right, rgba(17, 17, 17, 0) 0%, rgba(17, 17, 17, 255) 100%)',
|
||||
display: 'block',
|
||||
height: '100%',
|
||||
width: '10%',
|
||||
border: 'none',
|
||||
cursor: 'default',
|
||||
borderRadius: 0,
|
||||
} as CSSProperties,
|
||||
leftScrollButton: {
|
||||
left: 0,
|
||||
transform: 'scaleX(-1)'
|
||||
} as CSSProperties,
|
||||
rightScrollButton: {
|
||||
right: 0,
|
||||
} as CSSProperties,
|
||||
}
|
||||
|
||||
function StoriesPreview({ announcements, category }: StoriesPreviewProps) {
|
||||
const ulElement = useRef<HTMLUListElement | null>(null)
|
||||
const [showScrollButtons, setShowScrollButtons] = useState({ left: false, right: false })
|
||||
|
||||
const determineShowScrollButtons = (ul: HTMLUListElement) => (
|
||||
setShowScrollButtons({
|
||||
left: ul.scrollLeft > 0,
|
||||
right: ul.scrollLeft < (ul.scrollWidth - ul.clientWidth),
|
||||
})
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const ul = ulElement.current
|
||||
if (ul) {
|
||||
determineShowScrollButtons(ul)
|
||||
|
||||
const f = () => determineShowScrollButtons(ul)
|
||||
|
||||
ul.addEventListener('scroll', f)
|
||||
|
||||
return () => ul.removeEventListener('scroll', f)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const ul = ulElement.current
|
||||
|
||||
if (ul) {
|
||||
determineShowScrollButtons(ul)
|
||||
}
|
||||
}, [announcements])
|
||||
|
||||
const doScroll = (forward: boolean) => () => {
|
||||
const ul = ulElement.current
|
||||
if (ul) {
|
||||
const storyWidth = window.innerHeight * 0.25 * 9 / 16 + 10
|
||||
ul.scrollLeft += forward ? storyWidth : -storyWidth
|
||||
}
|
||||
}
|
||||
|
||||
return <div style={styles.container}>
|
||||
{showScrollButtons.left &&
|
||||
<Button onClick={doScroll(false)} style={{ ...styles.scrollButton, ...styles.leftScrollButton }}>
|
||||
<img src={rightAngleIcon} alt='Показать ещё' />
|
||||
</Button>
|
||||
}
|
||||
<ul style={styles.ul} className='StoriesPreview_ul' ref={ulElement}>
|
||||
{useMemo(() => announcements.map((ann, i) => (
|
||||
<li key={`${category}${i}`}>
|
||||
<Link to={'/?' + new URLSearchParams({
|
||||
userId: Number(-1).toString(),
|
||||
userCat: category,
|
||||
storyIndex: i.toString()
|
||||
}).toString()} style={styles.link}>
|
||||
{ann.src?.endsWith('mp4') ? (
|
||||
<video src={ann.src} style={styles.image} />
|
||||
) : (
|
||||
<img
|
||||
src={ann.src || categoryGraphics[ann.category]}
|
||||
alt={'Изображение' + (ann.src ? 'предмета' : categoryNames[ann.category])}
|
||||
style={styles.image}
|
||||
/>
|
||||
)}
|
||||
<p style={styles.title}>{ann.name}</p>
|
||||
<p style={styles.title}>{userCategoriesInfos[category](ann)}</p>
|
||||
</Link>
|
||||
</li>
|
||||
)), [announcements, category])}
|
||||
</ul>
|
||||
{showScrollButtons.right &&
|
||||
<Button onClick={doScroll(true)} style={{ ...styles.scrollButton, ...styles.rightScrollButton }}>
|
||||
<img src={rightAngleIcon} alt='Показать ещё' />
|
||||
</Button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
export default StoriesPreview
|
@ -7,6 +7,3 @@ export { default as TrashboxMarkers } from './TrashboxMarkers'
|
||||
export { default as WithToken } from './WithToken'
|
||||
export { default as ClickHandler } from './ClickHandler'
|
||||
export { default as AuthForm } from './AuthForm'
|
||||
export { default as BackHeader } from './BackHeader'
|
||||
export { default as CategoryPreview } from './CategoryPreview'
|
||||
export { default as StoriesPreview } from './StoriesPreview'
|
||||
|
@ -4,4 +4,3 @@ export { default as useAuth } from './useAuth'
|
||||
export { default as useTrashboxes } from './useTrashboxes'
|
||||
export { default as useAddAnnouncement } from './useAddAnnouncement'
|
||||
export { default as useOsmAddresses } from './useOsmAddress'
|
||||
export { default as useUser } from './useUser'
|
||||
|
@ -1,22 +0,0 @@
|
||||
import { initialUser } from '../../api/user'
|
||||
import { User } from '../../api/user/types'
|
||||
import { UseFetchErrored, UseFetchSucced } from '../useFetch'
|
||||
|
||||
const useUser = (): UseFetchSucced<User> | UseFetchErrored => (
|
||||
// useFetch(
|
||||
// composeUserUrl(getToken()),
|
||||
// 'GET',
|
||||
// true,
|
||||
// isUserResponse,
|
||||
// processUser,
|
||||
// initialUser
|
||||
// )
|
||||
|
||||
{
|
||||
data: initialUser,
|
||||
loading: false,
|
||||
error: null,
|
||||
}
|
||||
)
|
||||
|
||||
export default useUser
|
@ -70,8 +70,6 @@ function useFetch<R, T>(
|
||||
}
|
||||
}
|
||||
|
||||
export type { UseFetchErrored, UseFetchSucced }
|
||||
|
||||
export default useFetch
|
||||
|
||||
export { gotError, fallbackError }
|
||||
|
@ -1,26 +1,9 @@
|
||||
import { Container } from 'react-bootstrap'
|
||||
|
||||
import BackHeader from '../components/BackHeader'
|
||||
import { useUser } from '../hooks/api'
|
||||
import { userCategories } from '../assets/userCategories'
|
||||
import { CategoryPreview } from '../components'
|
||||
import { gotError } from '../hooks/useFetch'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
function UserPage() {
|
||||
const user = useUser()
|
||||
/* TODO */
|
||||
|
||||
return (
|
||||
<Container style={{ maxWidth: 'calc(100vh*9/16)' }}>
|
||||
<BackHeader text={
|
||||
gotError(user) ?
|
||||
user.error :
|
||||
`${user.data.name}, с нами с ${new Date(user.data.regDate).toLocaleDateString('ru')}`
|
||||
} />
|
||||
{userCategories.map(cat => (
|
||||
<CategoryPreview key={cat} category={cat} />
|
||||
))}
|
||||
</Container>
|
||||
)
|
||||
return <h1>For Yet Go <Link to='/'>Home</Link></h1>
|
||||
}
|
||||
|
||||
export default UserPage
|
||||
|
Loading…
x
Reference in New Issue
Block a user