Skip to content

Commit

Permalink
duplicate categories, seasons
Browse files Browse the repository at this point in the history
  • Loading branch information
Tschonti committed Jan 13, 2024
1 parent 9745cbe commit a0172a0
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 18 deletions.
16 changes: 14 additions & 2 deletions packages/client/src/api/hooks/categoryHooks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { Category, CategoryWithCriteria, CreateCategory, CreateResponse, EntityWithEditableIndicator, PontozoError } from '@pontozo/common'
import {
Category,
CategoryWithCriteria,
CategoryWithSeasons,
CreateCategory,
CreateResponse,
EntityWithEditableIndicator,
PontozoError,
} from '@pontozo/common'
import { useMutation, useQuery } from '@tanstack/react-query'
import { functionAxios } from '../../util/axiosConfig'

export const useFetchCategories = () => {
return useQuery<Category[], PontozoError>(['fetchCategories'], async () => (await functionAxios.get(`/categories`)).data, {
return useQuery<CategoryWithSeasons[], PontozoError>(['fetchCategories'], async () => (await functionAxios.get(`/categories`)).data, {
retry: false,
})
}
Expand Down Expand Up @@ -35,3 +43,7 @@ export const useUpdateCategoryMutation = (categoryId: number) => {
export const useDeleteCategoryMutation = (categoryId: number) => {
return useMutation<CreateResponse[], PontozoError>(async () => (await functionAxios.delete(`/categories/${categoryId}`)).data)
}

export const useDuplicateCategoryMutation = (categoryId: number) => {
return useMutation<Category, Error>(async () => (await functionAxios.post(`/categories/${categoryId}/duplicate`)).data)
}
4 changes: 4 additions & 0 deletions packages/client/src/api/hooks/seasonHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ export const useUpdateSeasonMutation = (seasonId: number) => {
export const useDeleteSeasonMutation = (seasonId: number) => {
return useMutation<CreateResponse[], PontozoError>(async () => (await functionAxios.delete(`/seasons/${seasonId}`)).data)
}

export const useDuplicateSeasonMutation = (seasonId: number) => {
return useMutation<Season, Error>(async () => (await functionAxios.post(`/seasons/${seasonId}/duplicate`)).data)
}
41 changes: 39 additions & 2 deletions packages/client/src/pages/categories/CategoryCreate.page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
import { Button, Flex, FormControl, FormErrorMessage, FormLabel, Heading, HStack, Input, Text, VStack } from '@chakra-ui/react'
import {
Alert,
AlertIcon,
AlertTitle,
Button,
Flex,
FormControl,
FormErrorMessage,
FormLabel,
Heading,
HStack,
Input,
useToast,
VStack,
} from '@chakra-ui/react'
import { CreateCategoryForm } from '@pontozo/common'
import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'
import { FaArrowLeft } from 'react-icons/fa'
Expand All @@ -9,6 +23,7 @@ import { NavigateWithError } from 'src/components/commons/NavigateWithError'
import {
useCreateCategoryMutation,
useDeleteCategoryMutation,
useDuplicateCategoryMutation,
useFetchCategory,
useUpdateCategoryMutation,
} from '../../api/hooks/categoryHooks'
Expand All @@ -35,9 +50,11 @@ export const CategoryCreatePage = () => {
formState: { errors },
} = form
const navigate = useNavigate()
const toast = useToast()
const createMutation = useCreateCategoryMutation()
const updateMutation = useUpdateCategoryMutation(categoryId)
const deleteMutation = useDeleteCategoryMutation(categoryId)
const duplicateMutation = useDuplicateCategoryMutation(categoryId)

const onSubmit: SubmitHandler<CreateCategoryForm> = ({ criteria, ...restOfData }) => {
if (categoryId === -1) {
Expand All @@ -47,6 +64,15 @@ export const CategoryCreatePage = () => {
}
}

const onDuplicateClick = () => {
duplicateMutation.mutate(undefined, {
onSuccess: (res) => {
navigate(`${PATHS.CATEGORIES}/${res.id}/edit`)
toast({ title: 'Kategória duplikálva!', description: 'Most már az újonnan létrejött kategóriát szerkeszted!', status: 'success' })
},
})
}

if (isLoading && isFetching) {
return <LoadingSpinner />
}
Expand All @@ -57,7 +83,13 @@ export const CategoryCreatePage = () => {
<VStack spacing={5} alignItems="flex-start">
<HelmetTitle title="Pontoz-O Admin | Kategória szerkesztése" />
<Heading>{categoryId === -1 ? 'Új kategória' : 'Kategória szerkesztése'}</Heading>
{!categoryEditable && <Text>Ez a kategória már nem szerkeszthető, mert része egy olyan szezonnak, ami már elkezdődött!</Text>}

{!categoryEditable && (
<Alert status="error">
<AlertIcon />
<AlertTitle>Ez a kategória nem szerkeszthető, mert része egy olyan szezonnak, ami már elkezdődött!</AlertTitle>
</Alert>
)}
<FormControl isInvalid={!!errors.name}>
<FormLabel>Név</FormLabel>
<Input {...register('name', { required: true, disabled: !categoryEditable })} bg="white" />
Expand All @@ -78,6 +110,11 @@ export const CategoryCreatePage = () => {
Vissza
</Button>
<HStack spacing={1}>
{categoryId > -1 && (
<Button colorScheme="brand" onClick={onDuplicateClick}>
Duplikálás
</Button>
)}
{categoryId > -1 && (
<ConfirmDialogButton
confirmAction={() => deleteMutation.mutate(undefined, { onSuccess: () => navigate(PATHS.CATEGORIES) })}
Expand Down
13 changes: 11 additions & 2 deletions packages/client/src/pages/categories/CategoryList.page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Button, Flex, Heading, Text, VStack } from '@chakra-ui/react'
import { Badge, Box, Button, Flex, Heading, HStack, Text, VStack } from '@chakra-ui/react'
import { Link } from 'react-router-dom'
import { HelmetTitle } from 'src/components/commons/HelmetTitle'
import { NavigateWithError } from 'src/components/commons/NavigateWithError'
Expand Down Expand Up @@ -26,7 +26,16 @@ export const CategoryListPage = () => {
<VStack spacing={3} alignItems="flex-start">
{data?.map((c) => (
<Box w="100%" as={Link} to={`${PATHS.CATEGORIES}/${c.id}/edit`} bg="white" borderRadius={6} borderWidth={1} p={2} key={c.id}>
<Heading size="sm">{c.name}</Heading>
<HStack justify="space-between">
<Heading size="sm">{c.name}</Heading>
<HStack>
{c.seasons.map((s) => (
<Badge variant="solid" colorScheme="brand" key={s.id}>
{s.name}
</Badge>
))}
</HStack>
</HStack>
<Text>{c.description}</Text>
</Box>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ export const CriteriaSelector = ({ editable }: { editable: boolean }) => {
<IconButton
aria-label="Szempont előrehozása"
size="xs"
isDisabled={idx === 0 ?? !editable}
isDisabled={idx === 0 || !editable}
icon={<FaCaretUp />}
colorScheme="brand"
onClick={() => moveCriterion(idx, c, -1)}
/>
<IconButton
aria-label="Szempont hátrébb vitele"
size="xs"
isDisabled={idx === watch('criteria').length - 1 ?? !editable}
isDisabled={idx === watch('criteria').length - 1 || !editable}
icon={<FaCaretDown />}
colorScheme="brand"
onClick={() => moveCriterion(idx, c, 1)}
Expand Down
11 changes: 9 additions & 2 deletions packages/client/src/pages/criteria/CriteriaCreate.page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {
Alert,
AlertIcon,
AlertTitle,
Badge,
Button,
Checkbox,
Expand All @@ -12,7 +15,6 @@ import {
SimpleGrid,
Stack,
Switch,
Text,
useToast,
VStack,
} from '@chakra-ui/react'
Expand Down Expand Up @@ -122,7 +124,12 @@ export const CriteriaCreatePage = () => {
</HStack>
</HStack>

{!criterionEditable && <Text>Ez a szempont már nem szerkeszthető, mert része egy olyan szezonnak, ami már elkezdődött!</Text>}
{!criterionEditable && (
<Alert status="error">
<AlertIcon />
<AlertTitle>Ez a szempont nem szerkeszthető, mert része egy olyan szezonnak, ami már elkezdődött!</AlertTitle>
</Alert>
)}
<FormControl isInvalid={!!errors.name}>
<FormLabel>Név</FormLabel>
<Input {...register('name', { required: true, disabled: !criterionEditable })} bg="white" />
Expand Down
35 changes: 32 additions & 3 deletions packages/client/src/pages/seasons/SeasonCreate.page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {
Alert,
AlertIcon,
AlertTitle,
Button,
Flex,
FormControl,
Expand All @@ -8,7 +11,6 @@ import {
HStack,
Input,
Stack,
Text,
useToast,
VStack,
} from '@chakra-ui/react'
Expand All @@ -21,7 +23,13 @@ import { ConfirmDialogButton } from 'src/components/commons/ConfirmDialogButton'
import { HelmetTitle } from 'src/components/commons/HelmetTitle'
import { NavigateWithError } from 'src/components/commons/NavigateWithError'
import { onError } from 'src/util/onError'
import { useCreateSeasonMutation, useDeleteSeasonMutation, useFetchSeason, useUpdateSeasonMutation } from '../../api/hooks/seasonHooks'
import {
useCreateSeasonMutation,
useDeleteSeasonMutation,
useDuplicateSeasonMutation,
useFetchSeason,
useUpdateSeasonMutation,
} from '../../api/hooks/seasonHooks'
import { LoadingSpinner } from '../../components/commons/LoadingSpinner'
import { PATHS } from '../../util/paths'
import { CategorySelector } from './components/CategorySelector'
Expand Down Expand Up @@ -51,6 +59,7 @@ export const SeasonCreatePage = () => {
const createMutation = useCreateSeasonMutation()
const updateMutation = useUpdateSeasonMutation(seasonId)
const deleteMutation = useDeleteSeasonMutation(seasonId)
const duplicateMutation = useDuplicateSeasonMutation(seasonId)

useEffect(() => {
if (seasonId !== -1 && data && !isLoading) {
Expand All @@ -64,6 +73,15 @@ export const SeasonCreatePage = () => {
}
}, [data, isLoading, seasonId, setValue])

const onDuplicateClick = () => {
duplicateMutation.mutate(undefined, {
onSuccess: (res) => {
navigate(`${PATHS.SEASONS}/${res.id}/edit`)
toast({ title: 'Szezon duplikálva!', description: 'Most már az újonnan létrejött szezont szerkeszted!', status: 'success' })
},
})
}

if (isLoading && isFetching) {
return <LoadingSpinner />
}
Expand All @@ -90,7 +108,13 @@ export const SeasonCreatePage = () => {
<VStack spacing={5} alignItems="flex-start">
<HelmetTitle title="Pontoz-O Admin | Szezon szerkesztése" />
<Heading>{seasonId === -1 ? 'Új szezon' : 'Szezon szerkesztése'}</Heading>
{!seasonEditable && <Text>Ez a szezon már nem szerkeszthető, mert már elkezdődött!</Text>}

{!seasonEditable && (
<Alert status="error">
<AlertIcon />
<AlertTitle>Ez a szezon nem szerkeszthető, mert már elkezdődött!</AlertTitle>
</Alert>
)}
<FormControl isInvalid={!!errors.name}>
<FormLabel>Név</FormLabel>
<Input {...register('name', { required: true, disabled: !seasonEditable })} bg="white" />
Expand Down Expand Up @@ -138,6 +162,11 @@ export const SeasonCreatePage = () => {
Vissza
</Button>
<HStack spacing={1}>
{seasonId > -1 && (
<Button colorScheme="brand" onClick={onDuplicateClick}>
Duplikálás
</Button>
)}
{seasonId > -1 && (
<ConfirmDialogButton
confirmAction={() =>
Expand Down
18 changes: 15 additions & 3 deletions packages/client/src/pages/seasons/components/CategorySelector.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {
Badge,
Box,
Button,
FormControl,
FormErrorMessage,
FormLabel,
Heading,
HStack,
IconButton,
Input,
Expand Down Expand Up @@ -173,7 +175,8 @@ export const CategorySelector = ({ editable }: { editable: boolean }) => {
<Text fontStyle="italic">Nincs találat</Text>
) : (
filteredCategoryList.map((c) => (
<Box
<VStack
alignItems="flex-start"
borderRadius={6}
borderWidth={1}
p={2}
Expand All @@ -185,8 +188,17 @@ export const CategorySelector = ({ editable }: { editable: boolean }) => {
onClose()
}}
>
<Text width="100%">{c.name}</Text>
</Box>
<Heading size="xs" width="100%">
{c.name}
</Heading>
<HStack>
{c.seasons.map((s) => (
<Badge variant="solid" colorScheme="brand" key={s.id}>
{s.name}
</Badge>
))}
</HStack>
</VStack>
))
)}
</VStack>
Expand Down
5 changes: 5 additions & 0 deletions packages/common/src/lib/types/categories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ArrayUnique, IsInt, IsNotEmpty, IsString, Min } from 'class-validator'
import { Criterion } from './criteria'
import { Season } from './seasons'

export interface Category {
id: number
Expand All @@ -25,3 +26,7 @@ export type CreateCategoryForm = Omit<CategoryWithCriteria, 'id'>
export interface CategoryWithCriteria extends Category {
criteria: Criterion[]
}

export interface CategoryWithSeasons extends Category {
seasons: Season[]
}
48 changes: 48 additions & 0 deletions packages/functions/src/functions/categories/duplicate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions'
import { PontozoException } from '@pontozo/common'
import { getUserFromHeaderAndAssertAdmin } from '../../service/auth.service'
import Category from '../../typeorm/entities/Category'
import { CategoryToCriterion } from '../../typeorm/entities/CategoryToCriterion'
import { getAppDataSource } from '../../typeorm/getConfig'
import { handleException } from '../../util/handleException'
import { validateId } from '../../util/validation'

export const duplicateCategory = async (req: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
try {
const user = await getUserFromHeaderAndAssertAdmin(req, context)
const id = validateId(req)

const ads = await getAppDataSource(context)
const oldCategory = await ads.manager.findOne(Category, {
where: { id },
relations: { criteria: true },
})
if (oldCategory === null) {
throw new PontozoException('A kategória nem található!', 404)
}

const newCategory = new Category()
newCategory.name = oldCategory.name
newCategory.description = oldCategory.description
newCategory.criteria = oldCategory.criteria.map((ctc) => {
const newCtc = new CategoryToCriterion()
newCtc.criterionId = ctc.criterionId
newCtc.order = ctc.order
return newCtc
})
await ads.manager.save(newCategory)

context.log(`User #${user.szemely_id} duplicated category #${oldCategory.id}`)
return {
jsonBody: newCategory,
}
} catch (error) {
return handleException(req, context, error)
}
}

app.http('categories-duplicate', {
methods: ['POST'],
route: 'categories/{id}/duplicate',
handler: duplicateCategory,
})
4 changes: 2 additions & 2 deletions packages/functions/src/functions/categories/getAll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ export const getCategories = async (req: HttpRequest, context: InvocationContext
await getUserFromHeaderAndAssertAdmin(req, context)

const categoryRepo = (await getAppDataSource(context)).getRepository(Category)
const categories = await categoryRepo.find({ relations: { criteria: true } })
const categories = await categoryRepo.find({ relations: { seasons: { season: true } } })
return {
jsonBody: categories,
jsonBody: categories.map((c) => ({ ...c, seasons: c.seasons.map((cts) => cts.season) })),
}
} catch (error) {
return handleException(req, context, error)
Expand Down
Loading

0 comments on commit a0172a0

Please sign in to comment.