Skip to content

Commit 379bddc

Browse files
committed
UI fixes in sponsor app
1 parent 285b7bd commit 379bddc

File tree

3 files changed

+189
-119
lines changed

3 files changed

+189
-119
lines changed
Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
.accreditationSection {
1+
.container {
22
display: flex;
33
flex-direction: column;
4+
justify-content: space-between;
45
height: 100%;
56
}
67

7-
.yMargin {
8-
margin: 20px 0;
8+
.title {
9+
font-size: 22px;
10+
font-weight: 700;
11+
line-height: 28px;
12+
letter-spacing: -0.33px;
13+
margin: 0;
14+
margin-bottom: 8px;
915
}
1016

1117
.description {
@@ -16,58 +22,41 @@
1622
margin-bottom: 24px;
1723
}
1824

19-
.personList {
25+
.inputContainer {
2026
display: flex;
2127
flex-direction: column;
22-
gap: 10px;
23-
flex-grow: 1;
24-
overflow-y: auto;
25-
margin-bottom: 20px;
26-
}
27-
28-
.listItem {
29-
display: flex;
30-
align-items: center;
31-
justify-content: space-between;
32-
gap: 16px;
33-
width: 100%;
34-
}
35-
36-
.listItem span {
37-
flex: 1;
38-
font-size: 16px;
39-
overflow-wrap: anywhere;
28+
margin-bottom: 16px;
4029
}
4130

4231
.button {
4332
display: flex;
44-
justify-content: center;
4533
align-items: center;
34+
justify-content: center;
35+
text-align: center;
36+
line-height: 1;
4637
border-radius: 12px;
38+
background: #fff;
4739
width: 100%;
48-
height: 56px;
4940
cursor: pointer;
50-
font-weight: 700;
51-
font-size: 16px;
41+
margin-top: auto;
42+
font-weight: bold;
5243
border: none;
53-
transition: all 0.2s ease-in-out;
54-
55-
&.primary {
56-
background: #ffffff;
57-
transition: background-color 0.1s ease-in-out;
44+
margin-top: 20px;
45+
transition: background-color 0.1s ease-in-out;
46+
padding: 0 16px;
47+
height: 56px;
48+
flex-shrink: 0;
5849

59-
&:hover {
60-
background: color-mix(in srgb, #ffffff 90%, #000000 10%);
61-
}
50+
&:hover {
51+
background-color: color-mix(in srgb, #fff 90%, #000 10%);
6252
}
6353

64-
&.secondary {
65-
min-width: 120px;
66-
max-width: 160px;
67-
height: 56px;
54+
&.smallButton {
55+
gap: 8px;
6856
background: #080407;
6957
color: #ffffff;
7058
border: 1px solid #ffffff33;
59+
margin-bottom: 16px;
7160
transition:
7261
background 0.3s ease-in-out,
7362
color 0.3s ease-in-out;
@@ -77,27 +66,63 @@
7766
color: #000000;
7867
}
7968

80-
&.remove {
81-
min-width: 100px;
82-
max-width: 110px;
83-
}
84-
85-
&.remove::before {
86-
content: '-';
87-
font-weight: 700;
88-
font-size: 24px;
89-
margin-right: 4px;
90-
}
91-
&.add::before {
69+
&::before {
9270
content: '+';
9371
font-weight: 700;
9472
font-size: 24px;
95-
margin-right: 4px;
73+
margin-right: 8px;
9674
}
9775
}
9876
}
9977

100-
.saveButton {
101-
margin-top: auto;
78+
.addPersonContainer {
79+
display: flex;
80+
flex-direction: column;
81+
}
82+
83+
.smallButton {
84+
width: 50%;
85+
align-self: center;
86+
margin: 0 auto;
87+
}
88+
89+
.personList {
90+
list-style: none;
91+
padding: 0;
92+
margin: 0 0 16px 0;
93+
display: flex;
94+
flex-direction: column;
95+
gap: 8px;
96+
}
97+
98+
.personRow {
99+
display: flex;
100+
gap: 8px;
101+
margin-top: 8px;
102+
}
103+
104+
.personItem {
105+
display: flex;
102106
width: 100%;
107+
background: #1a1a1a;
108+
padding: 12px;
109+
border-radius: 8px;
110+
color: #fff;
111+
}
112+
113+
.removePersonButton {
114+
background: #080407;
115+
color: #ffffff;
116+
border: 1px solid #ffffff33;
117+
border-radius: 8px;
118+
padding: 6px 12px;
119+
cursor: pointer;
120+
font-weight: bold;
121+
transition: background 0.3s ease-in-out, color 0.3s ease-in-out;
122+
123+
&:hover {
124+
background: #ffffff;
125+
color: #000000;
126+
}
103127
}
128+
Lines changed: 109 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import clsx from 'clsx';
2-
import { useState } from 'react';
1+
import { useEffect, useState } from 'react';
32
import toast from 'react-hot-toast';
43

54
import { useCompanyGetCurrentPublic } from '../../api/company/useCompanyGetCurrentPublic';
@@ -9,106 +8,154 @@ import { Input } from '../../components/Input';
98
import { FormComponent } from '../../types/form';
109
import c from './Accreditation.module.scss';
1110

12-
const isPersonNameValid = (person: string) => {
13-
if (!person || person.length < 2 || person.length > 50 || /\d/.test(person)) {
14-
toast.error('Molimo vas unesite validno ime i prezime');
15-
return false;
16-
}
11+
interface Person {
12+
fullName: string;
13+
email: string;
14+
}
1715

18-
if (person.split(/\s+/).length < 2) {
19-
toast.error('Molimo vas unesite ime i prezime osobe');
20-
return false;
16+
// Parse stored string format "Name | email" to Person object
17+
const parsePersonString = (str: string): Person => {
18+
const parts = str.split(' | ');
19+
if (parts.length === 2) {
20+
return { fullName: parts[0], email: parts[1] };
2121
}
22+
// Fallback for old format (just name)
23+
return { fullName: str, email: '' };
24+
};
2225

23-
return true;
26+
// Convert Person object to stored string format
27+
const personToString = (person: Person): string => {
28+
return `${person.fullName} | ${person.email}`;
2429
};
2530

2631
export const Accreditation: FormComponent = ({ close }) => {
27-
const [personName, setPersonName] = useState<string>('');
32+
const [fullName, setFullName] = useState('');
33+
const [email, setEmail] = useState('');
34+
const [people, setPeople] = useState<Person[]>([]);
2835

29-
const { data: companyData } = useCompanyGetCurrentPublic();
36+
const { data: companyData, isLoading, error } = useCompanyGetCurrentPublic();
3037
const { mutateAsync: updateAccreditation } = useCompanyUpdateAccreditation();
3138
const { mutateAsync: createUser } = useCreateUser();
3239

33-
const [people, setPeople] = useState<string[]>(
34-
companyData?.peopleForAccreditation ?? [],
35-
);
40+
useEffect(() => {
41+
if (companyData?.peopleForAccreditation) {
42+
setPeople(companyData.peopleForAccreditation.map(parsePersonString));
43+
}
44+
}, [companyData]);
45+
46+
if (error) return <div>{error.toString()}</div>;
47+
if (isLoading) return <div>Loading...</div>;
3648

3749
const handleAddPerson = () => {
38-
if (!isPersonNameValid(personName.trim())) {
50+
const trimmedName = fullName.trim();
51+
const trimmedEmail = email.trim();
52+
53+
if (trimmedName.length < 2 || trimmedName.match(/\d/)) {
54+
toast.error('Unesite valjano ime i prezime');
55+
return;
56+
}
57+
58+
if (trimmedName.split(/\s+/).length < 2) {
59+
toast.error('Molimo vas unesite ime i prezime osobe');
3960
return;
4061
}
4162

42-
setPeople([...people, personName.trim().replace(/\s+/g, ' ')]);
43-
setPersonName('');
63+
if (!/\S+@\S+\.\S+/.test(trimmedEmail)) {
64+
toast.error('Unesite valjanu email adresu');
65+
return;
66+
}
67+
68+
setPeople([...people, { fullName: trimmedName, email: trimmedEmail }]);
69+
setFullName('');
70+
setEmail('');
4471
};
4572

46-
const handleRemovePerson = (name: string) => {
47-
setPeople(people.filter((p) => p != name));
73+
const handleRemovePerson = (emailToRemove: string) => {
74+
setPeople(people.filter((p) => p.email !== emailToRemove));
4875
};
4976

5077
const handleSave = async () => {
5178
try {
52-
await updateAccreditation(people);
79+
const peopleStrings = people.map(personToString);
80+
await updateAccreditation(peopleStrings);
5381

5482
const existingPeople = companyData?.peopleForAccreditation ?? [];
55-
const newPeople = people.filter((p) => !existingPeople.includes(p));
83+
const existingParsed = existingPeople.map(parsePersonString);
84+
const newPeople = people.filter(
85+
(p) => !existingParsed.some((ep) => ep.email === p.email),
86+
);
5687

57-
newPeople.map(async (fullName) => {
58-
const [firstName, ...lastNameParts] = fullName.split(' ');
88+
for (const person of newPeople) {
89+
const [firstName, ...lastNameParts] = person.fullName.split(' ');
5990
const lastName = lastNameParts.join(' ');
6091

61-
if (!firstName || !lastName) return;
92+
if (!firstName || !lastName) continue;
6293

6394
await createUser({ firstName, lastName });
95+
}
6496

65-
close();
66-
});
97+
close();
6798
} catch (error) {
6899
toast.error('Došlo je do greške pri spremanju akreditacija.');
69100
console.error(error);
70101
}
71102
};
72103

73104
return (
74-
<section className={c.accreditationSection}>
75-
<h1>Akreditacije</h1>
76-
<p className={clsx(c.yMargin, c.description)}>
77-
Dodajte osobe koje će prisustvovati na DUMP DAYS
78-
</p>
79-
105+
<div className={c.container}>
80106
<div>
81-
<Input
82-
value={personName}
83-
label='Ime osobe'
84-
onChange={(value) => setPersonName(value)}
85-
/>
86-
87-
<button
88-
onClick={handleAddPerson}
89-
className={clsx(c.yMargin, c.button, c.secondary, c.add)}>
90-
Dodaj osobu
91-
</button>
92-
</div>
93-
94-
<ul className={c.personList}>
95-
{people.map((person) => (
96-
<li key={person} className={c.listItem}>
97-
<span className={c.yMargin}>{person}</span>
107+
<h1 className={c.title}>Akreditacije</h1>
108+
<p className={c.description}>
109+
Dodajte osobe koje će prisustvovati na DUMP DAYS
110+
</p>
111+
112+
<div className={c.inputContainer}>
113+
<label>Osobe za akreditaciju</label>
114+
115+
<ul className={c.personList}>
116+
{people.map((person) => (
117+
<div key={person.email} className={c.personRow}>
118+
<li className={c.personItem}>
119+
<span>{person.fullName}</span>
120+
<span style={{ opacity: 0.7, marginLeft: '8px' }}>
121+
{person.email}
122+
</span>
123+
</li>
124+
<button
125+
type='button'
126+
onClick={() => handleRemovePerson(person.email)}
127+
className={c.removePersonButton}>
128+
Ukloni
129+
</button>
130+
</div>
131+
))}
132+
</ul>
133+
134+
<div className={c.addPersonContainer}>
135+
<div
136+
style={{ display: 'flex', gap: '16px', marginBottom: '16px' }}>
137+
<Input
138+
value={fullName}
139+
onChange={(val) => setFullName(val)}
140+
label='Ime i prezime'
141+
/>
142+
<Input
143+
value={email}
144+
onChange={(val) => setEmail(val)}
145+
label='Email'
146+
/>
147+
</div>
98148
<button
99-
onClick={() => handleRemovePerson(person)}
100-
className={clsx(c.button, c.secondary, c.remove)}>
101-
Ukloni
149+
onClick={handleAddPerson}
150+
className={`${c.smallButton} ${c.button}`}>
151+
Dodaj osobu
102152
</button>
103-
</li>
104-
))}
105-
</ul>
106-
107-
<button
108-
onClick={handleSave}
109-
className={clsx(c.button, c.primary, c.saveButton)}>
153+
</div>
154+
</div>
155+
</div>
156+
<button onClick={handleSave} className={c.button}>
110157
Spremi
111158
</button>
112-
</section>
159+
</div>
113160
);
114161
};

0 commit comments

Comments
 (0)