Skip to content

Commit f2c4db7

Browse files
committed
fix(clean-up): added alert and cleanup
1 parent 4a1fb4e commit f2c4db7

25 files changed

Lines changed: 386 additions & 153 deletions

File tree

app/components/FileUpload/index.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
import { UploadFillIcon } from '@ifrc-go/icons';
2-
import { RawFileInput } from '@ifrc-go/ui';
1+
import { Activity } from 'react';
2+
import {
3+
AlarmWarningFillIcon,
4+
AlertFillIcon,
5+
UploadFillIcon,
6+
} from '@ifrc-go/icons';
7+
import {
8+
InputError,
9+
RawFileInput,
10+
} from '@ifrc-go/ui';
311

412
import styles from './styles.module.css';
513

@@ -8,6 +16,7 @@ interface Props {
816
onChange: (file?: File) => void;
917
name?: string;
1018
accept?: string
19+
error?: string;
1120
}
1221

1322
function FileUpload(props: Props) {
@@ -16,11 +25,12 @@ function FileUpload(props: Props) {
1625
onChange,
1726
name,
1827
accept,
28+
error,
1929
} = props;
2030

2131
return (
2232
<div className={styles.fileInput}>
23-
<div>
33+
<div className={styles.inputSection}>
2434
<RawFileInput
2535
name={name}
2636
variant="secondary"
@@ -30,12 +40,17 @@ function FileUpload(props: Props) {
3040
<UploadFillIcon />
3141
Upload
3242
</RawFileInput>
43+
<p>
44+
{value?.name ? (
45+
value.name
46+
) : 'No document selected'}
47+
</p>
3348
</div>
34-
<p>
35-
{value?.name ? (
36-
value.name
37-
) : 'No document selected'}
38-
</p>
49+
<Activity mode={error ? 'visible' : 'hidden'}>
50+
<InputError>
51+
{error}
52+
</InputError>
53+
</Activity>
3954
</div>
4055
);
4156
}
Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
.fileInput {
22
display: flex;
3-
flex-direction: row;
3+
flex-direction: column;
44
width: 100%;
55
gap: var(--go-ui-spacing-sm);
6-
@media (max-width: 768px) {
7-
flex-direction: column;
6+
.inputSection {
7+
display: flex;
8+
align-items: center;
9+
gap: var(--go-ui-spacing-sm);
10+
11+
@media (max-width: 768px) {
12+
flex-direction: column;
13+
}
814
}
915
p {
1016
word-break: break-all;
1117
}
12-
}
18+
}

app/components/RichTextEditor/index.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import '@mdxeditor/editor/style.css';
22

33
import {
4+
Activity,
45
useEffect,
56
useMemo,
67
useRef,
78
} from 'react';
9+
import { InputError } from '@ifrc-go/ui';
810
import {
911
BlockTypeSelect,
1012
BoldItalicUnderlineToggles,
@@ -30,6 +32,7 @@ import styles from './styles.module.css';
3032
interface Props {
3133
value?: string;
3234
onChange?: (value: string) => void;
35+
error?: string;
3336
}
3437

3538
function debounce<Args extends unknown[], R>(
@@ -59,7 +62,12 @@ function ToolbarContents() {
5962
);
6063
}
6164

62-
export default function RichTextEditor({ value = '', onChange }: Props) {
65+
export default function RichTextEditor(props: Props) {
66+
const {
67+
value = '',
68+
onChange,
69+
error,
70+
} = props;
6371
const ref = useRef<MDXEditorMethods>(null);
6472
const prevValueRef = useRef(value);
6573

@@ -101,6 +109,11 @@ export default function RichTextEditor({ value = '', onChange }: Props) {
101109
placeholder="Start writing here..."
102110
plugins={plugins}
103111
/>
112+
<Activity mode={error ? 'visible' : 'hidden'}>
113+
<InputError>
114+
{error}
115+
</InputError>
116+
</Activity>
104117
</div>
105118
);
106119
}

app/views/Blog/BlogForm/index.tsx

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
2+
Activity,
23
useCallback,
34
useEffect,
5+
useMemo,
46
} from 'react';
57
import {
68
useNavigate,
@@ -36,6 +38,7 @@ import {
3638
useDepartmentAndDirectiveQuery,
3739
useUpdateBlogMutation,
3840
} from '#generated/types/graphql';
41+
import useAlert from '#hooks/useAlert';
3942

4043
type PartialFormType = PartialForm<BlogCreateInput> &
4144
{ createdBy: string, modifiedBy: string, slug: string | null };
@@ -97,6 +100,8 @@ const defaultEditFormValue: PartialFormType = {
97100

98101
function BlogForm() {
99102
const { id } = useParams();
103+
const alert = useAlert();
104+
100105
const navigate = useNavigate();
101106
const [{ data }] = useBlogDetailQueryQuery({
102107
variables: { id: id || '' }, pause: !id,
@@ -137,42 +142,53 @@ function BlogForm() {
137142
});
138143
if (res.data?.updateBlog?.ok) {
139144
navigate('/blog');
145+
alert.show('Blog updated successfully', { variant: 'success' });
140146
} else if (res.data?.updateBlog?.errors) {
147+
const errorMessages = res.data?.updateBlog?.errors;
141148
setError(res.data.updateBlog.errors);
149+
alert.show(errorMessages, { variant: 'danger' });
142150
}
143151
} else {
144152
const res = await createBlogMutate({
145153
data: mutateData,
146154
});
147155
if (res.data?.createBlog.ok) {
148156
navigate('/blog');
157+
alert.show('Blog created successfully', { variant: 'success' });
149158
} else if (res.data?.createBlog?.errors) {
159+
const errorMessages = res.data?.createBlog?.errors;
150160
setError(res.data.createBlog.errors);
161+
alert.show(errorMessages, { variant: 'danger' });
151162
}
152163
}
153164
},
154165
);
155166
handler();
156-
}, [setError, validate, id, updateBlogMutate, createBlogMutate, navigate]);
167+
}, [setError, alert, validate, id, updateBlogMutate, createBlogMutate, navigate]);
157168

158-
const departmentOptions = departmentAndDirective?.departments.results.map(
159-
(dept) => ({
169+
const departmentOptions = useMemo(
170+
() => departmentAndDirective?.departments.results.map((dept) => ({
160171
id: dept.id,
161172
name: dept.title,
162-
}),
163-
) ?? [];
173+
})) ?? [],
174+
[departmentAndDirective?.departments.results],
175+
);
164176

165-
const directiveOptions = departmentAndDirective?.strategicDirectives.results.map(
166-
(directive) => ({
177+
const directiveOptions = useMemo(
178+
() => departmentAndDirective?.strategicDirectives.results.map((directive) => ({
167179
id: directive.id,
168180
name: directive.title,
169-
}),
170-
) ?? [];
181+
})) ?? [],
182+
[departmentAndDirective?.strategicDirectives.results],
183+
);
171184

172-
const statusOptions = Object.values(StatusEnum).map((status) => ({
173-
value: status,
174-
label: status,
175-
}));
185+
const statusOptions = useMemo(
186+
() => Object.values(StatusEnum).map((status) => ({
187+
value: status,
188+
label: status,
189+
})),
190+
[],
191+
);
176192

177193
useEffect(() => {
178194
if (data?.blog) {
@@ -191,11 +207,12 @@ function BlogForm() {
191207
setFieldValue(`${blog.createdBy.firstName} ${blog.createdBy.lastName}`, 'createdBy');
192208
}
193209
}, [data, setFieldValue]);
210+
194211
return (
195212
<Page>
196213
<ContainerWrapper>
197-
<FormSection headingLevel={3} label="BLOG DETAIL" />
198-
{(value.createdBy && value.modifiedBy) && (
214+
<FormSection headingLevel={3} label={id ? 'BLOG DETAIL' : 'CREATE BLOG'} />
215+
<Activity mode={value.createdBy && value.modifiedBy ? 'visible' : 'hidden'}>
199216
<FormSection>
200217
<Heading level={6}>
201218
Created by:
@@ -208,13 +225,15 @@ function BlogForm() {
208225
{value.createdBy}
209226
</Heading>
210227
</FormSection>
211-
)}
228+
</Activity>
212229
<FormSection label="Title" description="Enter the title name of the Blog" withAsteriskOnTitle>
213230
<TextInput
214231
name="title"
232+
autoFocus
215233
value={value.title}
216234
error={error?.title as string}
217235
onChange={setFieldValue}
236+
placeholder="title"
218237
/>
219238
</FormSection>
220239
<FormSection label="Published Date" description="This date should be the Published Date of the blog" withAsteriskOnTitle>
@@ -232,6 +251,7 @@ function BlogForm() {
232251
value={value.author}
233252
onChange={setFieldValue}
234253
error={error?.author as string}
254+
placeholder="author"
235255
/>
236256
</FormSection>
237257
<FormSection label="Cover photo" description="Add a cover photo, which will be displayed on top" withAsteriskOnTitle>
@@ -240,6 +260,7 @@ function BlogForm() {
240260
onChange={(files) => setFieldValue(files, 'coverImage')}
241261
accept="audio/*"
242262
value={value.coverImage}
263+
error={error?.coverImage as string}
243264
/>
244265
</FormSection>
245266
<FormSection label="Featured" description="Click on the checkbox if the blog is to be featured" withAsteriskOnTitle>
@@ -263,16 +284,17 @@ function BlogForm() {
263284
error={error?.status}
264285
/>
265286
</FormSection>
266-
{value.slug && (
287+
<Activity mode={value.slug ? 'visible' : 'hidden'}>
267288
<FormSection label="Slug" description="Unique URL identifier for the blog">
268289
<TextInput
269290
name="slug"
270291
value={value.slug ?? ''}
271292
onChange={(val) => setFieldValue(val || null, 'slug')}
272293
error={error?.slug}
294+
readOnly
273295
/>
274296
</FormSection>
275-
)}
297+
</Activity>
276298
<FormSection label="Strategic Directive (NS)" description="Select under which strategic directive it belongs">
277299
<SelectInput
278300
name="directive"
@@ -302,6 +324,7 @@ function BlogForm() {
302324
<RichTextEditor
303325
value={value.content}
304326
onChange={(val) => setFieldValue(val, 'content')}
327+
error={error?.content}
305328
/>
306329
</FormSection>
307330
<FormSection>

app/views/Blog/BlogList/index.tsx

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ import {
2323
useBlogQueryQuery,
2424
useDeleteBlogMutation,
2525
} from '#generated/types/graphql';
26+
import useAlert from '#hooks/useAlert';
2627
import usePagination from '#hooks/usePagination';
2728

2829
type BlogListType = NonNullable<BlogQueryQuery['blogs']>['results'][number];
2930

3031
function BlogList() {
3132
const navigate = useNavigate();
33+
const alert = useAlert();
3234
const {
3335
page,
3436
setPage,
@@ -48,69 +50,49 @@ function BlogList() {
4850
deleteBlog({ id }).then((resp) => {
4951
if (resp.data?.deleteBlog) {
5052
reExecuteQuery();
53+
alert.show('Blog deleted successfully', { variant: 'success' });
5154
closeModal();
5255
}
5356
});
5457
},
55-
[deleteBlog, reExecuteQuery],
58+
[deleteBlog, reExecuteQuery, alert],
5659
);
5760
const columns = useMemo(
5861
() => ([
59-
// Serial Number
6062
createNumberColumn<BlogListType & { sn: number }, string | number>(
6163
'sn',
6264
'S.N.',
6365
(item) => item.sn,
6466
{ columnWidth: 60 },
6567
),
66-
// Title
6768
createStringColumn<BlogListType, string | number>(
6869
'title',
6970
'Title',
7071
(blog) => blog.title,
71-
{
72-
sortable: true,
73-
},
7472
),
7573

76-
// Published Date
7774
createDateColumn<BlogListType, string | number>(
7875
'publishedDate',
7976
'Published Date',
8077
(blog) => blog.publishedDate,
81-
{
82-
sortable: true,
83-
},
8478
),
8579

86-
// Author
8780
createStringColumn<BlogListType, string | number>(
8881
'author',
8982
'Author',
9083
(blog) => blog.author,
91-
{
92-
sortable: true,
93-
},
9484
),
9585

96-
// Featured
9786
createBooleanColumn<BlogListType, string | number>(
9887
'featured',
9988
'Featured',
10089
(blog) => blog.featured,
101-
{
102-
sortable: true,
103-
},
10490
),
10591

106-
// Status
10792
createStringColumn<BlogListType, string | number>(
10893
'status',
10994
'Status',
11095
(blog) => blog.status,
111-
{
112-
sortable: true,
113-
},
11496
),
11597

11698
createElementColumn<BlogListType, string | number, TableActionsProps>(

0 commit comments

Comments
 (0)