Skip to content

Commit f93e453

Browse files
authored
Merge pull request #623 from dump-hr/lukazuljevic/admin-app-improvements
Add export feature; Add date of agreement with sponsors
2 parents 779f0a0 + 3050f98 commit f93e453

File tree

18 files changed

+319
-19
lines changed

18 files changed

+319
-19
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
.dragNDropWrapper {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 12px;
5+
width: 100%;
6+
max-width: 400px;
7+
}
8+
9+
.dragNDropArea {
10+
border: 1px dashed #ccc;
11+
padding: 30px;
12+
text-align: center;
13+
cursor: pointer;
14+
display: flex;
15+
flex-direction: column;
16+
justify-content: center;
17+
align-items: center;
18+
width: 100%;
19+
height: 150px;
20+
box-sizing: border-box;
21+
}
22+
23+
.dragNDropAreaText {
24+
font-size: 16px;
25+
color: #bbb;
26+
width: 100%;
27+
}
28+
29+
.previewWrapper {
30+
display: flex;
31+
flex-direction: column;
32+
justify-content: center;
33+
align-items: center;
34+
width: 100%;
35+
max-width: 400px;
36+
gap: 8px;
37+
}
38+
39+
.img {
40+
display: block;
41+
max-width: 100%;
42+
max-height:350px;
43+
object-fit: contain;
44+
border-radius: 4px;
45+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useDropzone } from 'react-dropzone';
2+
3+
import { Button } from '../Button';
4+
import c from './FileUpload.module.scss';
5+
6+
type FileUploadProps = {
7+
src: string | ArrayBuffer | null | undefined;
8+
label?: string;
9+
accept?: string | undefined;
10+
handleUpload: (files: File[]) => void;
11+
handleRemove: () => void;
12+
};
13+
14+
export const FileUpload: React.FC<FileUploadProps> = ({
15+
src,
16+
label = null,
17+
accept = 'image/*',
18+
handleUpload,
19+
handleRemove,
20+
}) => {
21+
const { getRootProps, getInputProps, isDragActive } = useDropzone({
22+
onDrop: (acceptedFiles: File[]) => {
23+
handleUpload(acceptedFiles);
24+
},
25+
});
26+
27+
return (
28+
<div className={c.dragNDropWrapper}>
29+
{label && <p>{label}</p>}
30+
{!src && (
31+
<div className={c.dragNDropArea} {...getRootProps()}>
32+
<input accept={accept} {...getInputProps()} />
33+
{isDragActive ? (
34+
<p className={c.dragNDropAreaText}>Drop the files here ...</p>
35+
) : (
36+
<p className={c.dragNDropAreaText}>
37+
Drag 'n' drop some files here, or click to select files
38+
</p>
39+
)}
40+
</div>
41+
)}
42+
43+
{src && (
44+
<div className={c.previewWrapper}>
45+
<img className={c.img} src={src as string} alt='preview' />
46+
<Button onClick={() => handleRemove()}>Remove</Button>
47+
</div>
48+
)}
49+
</div>
50+
);
51+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './FileUpload';

apps/admin/src/components/PotentialSponsorsTable/PotentialSponsorsTable.module.scss

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535

3636

3737
select {
38-
@extend .buttonBase;
39-
padding: 12px 12px;
38+
@extend .buttonBase;
39+
padding: 12px 12px;
4040
background: #F0F0F0;
4141
border: none;
4242
color: #929292;
@@ -58,11 +58,11 @@
5858
border-bottom: 1px solid #ddd;
5959
background: white;
6060
font-weight: bold;
61-
min-width: 140px;
61+
min-width: 125px;
6262
vertical-align: middle;
6363

6464
}
65-
.numberCol {
65+
.numberCol {
6666
width: 40px;
6767
min-width: 40px;
6868
text-align: center;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { RewardModifyDto } from '@ddays-app/types';
2+
import { classValidatorResolver } from '@hookform/resolvers/class-validator';
3+
import { useForm } from 'react-hook-form';
4+
5+
import { useRewardCreate } from '../api/reward/useRewardCreate';
6+
import { useRewardGetOne } from '../api/reward/useRewardGetOne';
7+
import { useRewardUpdate } from '../api/reward/useRewardUpdate';
8+
import { useRewardUpdateImage } from '../api/reward/useRewardUpdateImage';
9+
import { useRewardRemoveImage } from '../api/reward/useRewardRemoveImage';
10+
11+
import { Button } from '../components/Button';
12+
import { InputHandler } from '../components/InputHandler';
13+
import { FileUpload } from '../components/FileUpload';
14+
import { Question, QuestionType } from '../types/question';
15+
16+
import c from './Form.module.scss';
17+
18+
type RewardFormProps = {
19+
id?: number;
20+
onSuccess: () => void;
21+
};
22+
23+
export const RewardForm: React.FC<RewardFormProps> = ({ id, onSuccess }) => {
24+
const { data: reward, isLoading } = useRewardGetOne(id);
25+
26+
const createReward = useRewardCreate();
27+
const updateReward = useRewardUpdate();
28+
const updateImage = useRewardUpdateImage();
29+
const removeImage = useRewardRemoveImage();
30+
31+
const questions: Question[] = [
32+
{
33+
id: 'name',
34+
type: QuestionType.Field,
35+
title: 'Naziv',
36+
defaultValue: reward?.name,
37+
},
38+
{
39+
id: 'description',
40+
type: QuestionType.TextArea,
41+
title: 'Opis',
42+
defaultValue: reward?.description,
43+
},
44+
];
45+
46+
const form = useForm<RewardModifyDto>({
47+
resolver: classValidatorResolver(RewardModifyDto),
48+
defaultValues: {
49+
name: reward?.name ?? '',
50+
description: reward?.description ?? '',
51+
},
52+
});
53+
54+
const handleImageUpload = async (files: File[]) => {
55+
if (!id) return;
56+
await updateImage.mutateAsync({ id, file: files[0] });
57+
};
58+
59+
const handleImageRemove = async () => {
60+
if (!id) return;
61+
await removeImage.mutateAsync(id);
62+
};
63+
64+
if (id && isLoading) {
65+
return <div>Loading...</div>;
66+
}
67+
68+
return (
69+
<div className={c.formContainer}>
70+
{questions.map((q) => (
71+
<InputHandler question={q} form={form} key={q.id} />
72+
))}
73+
74+
{id && (
75+
<>
76+
<p>Slika:</p>
77+
<FileUpload
78+
src={reward?.imageUrl}
79+
handleUpload={handleImageUpload}
80+
handleRemove={handleImageRemove}
81+
/>
82+
</>
83+
)}
84+
85+
<Button
86+
onClick={form.handleSubmit(async (formData) => {
87+
if (id) {
88+
await updateReward.mutateAsync({ ...formData, id });
89+
} else {
90+
await createReward.mutateAsync(formData);
91+
}
92+
93+
onSuccess();
94+
})}>
95+
Submit
96+
</Button>
97+
</div>
98+
);
99+
};

apps/admin/src/components/SponsorContractsTable/tableColumnsContracts.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ export const tableColumns = [
44
{ label: '#', key: 'number', className: c.numberCol },
55
{ label: 'Firma', key: 'company' },
66
{ label: 'Predstavnik', key: 'representative' },
7-
{ label: 'Ime', key: 'name' },
8-
{ label: 'Adresa', key: 'address' },
9-
{ label: 'OIB', key: 'oib' },
7+
{ label: 'Ime firme d.o.o', key: 'name' },
8+
{ label: 'Adresa firme', key: 'address' },
9+
{ label: 'OIB firme', key: 'oib' },
1010
{ label: 'Predstavnik firme', key: 'companyRepresentative' },
1111
{
1212
label: 'Poz. predstavnika',

apps/admin/src/components/SponsorMaterialsTable/SponsorMaterialsTable.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ export const SponsorMaterialsTable = ({
117117
)}
118118
</React.Fragment>
119119
))}
120+
121+
<td style={{ textAlign: 'center' }}>
122+
{new Date(material.createdOn).toLocaleDateString('hr-HR', {
123+
day: '2-digit',
124+
month: '2-digit',
125+
year: 'numeric',
126+
})}
127+
</td>
120128
</tr>
121129
))}
122130
</tbody>

apps/admin/src/components/SponsorMaterialsTable/tableColumnsMaterials.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ export const tableColumns = [
1919
{ label: 'Oprema', key: 'equipment', small: true },
2020
{ label: 'Osobe za akr.', key: 'peopleForAccreditation', small: true },
2121
{ label: 'Uneseni u app', key: 'insertedIntoApp', small: true },
22+
{ label: 'Datum pristanka', key: 'createdOn' },
2223
];

apps/admin/src/components/TableDashboard/TableActions/TableActions.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type TableActionsProps = {
1212
onCreateNew?: () => void;
1313
onEdit?: () => void;
1414
onDelete?: () => void;
15+
onExport?: () => void;
1516
};
1617

1718
export const TableActions: React.FC<TableActionsProps> = ({
@@ -22,6 +23,7 @@ export const TableActions: React.FC<TableActionsProps> = ({
2223
onCreateNew,
2324
onEdit,
2425
onDelete,
26+
onExport,
2527
}) => {
2628
return (
2729
<div className={c.actions}>
@@ -31,7 +33,7 @@ export const TableActions: React.FC<TableActionsProps> = ({
3133
Dodaj
3234
</button>
3335

34-
<button type='button' className={c.grayButton}>
36+
<button type='button' className={c.grayButton} onClick={onExport}>
3537
<ExportIcon />
3638
Izvezi
3739
</button>

apps/admin/src/components/TableDashboard/TableDashboard.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { TableActions } from './TableActions';
55
import { Table } from './Table';
66
import { DataRow, Direction } from '../../types/table';
77
import toast from 'react-hot-toast';
8+
import { exportToCSV } from '../../helpers/exportToCsv';
89

910
type TableDashboardProps = {
1011
data: DataRow[];
@@ -144,6 +145,9 @@ export const TableDashboard: React.FC<TableDashboardProps> = ({
144145
onDelete={
145146
onDelete ? () => selected.length > 0 && onDelete(selected) : undefined
146147
}
148+
onExport={() =>
149+
exportToCSV(sortedData, `${dataType || 'table'}-export.csv`)
150+
}
147151
/>
148152

149153
<Table

0 commit comments

Comments
 (0)