source: frontend/src/pages/Dashboard/components/AddCustomCategoryModal.jsx@ b3d5fed

Last change on this file since b3d5fed was e9a0543, checked in by Andrej <asumanovski@…>, 2 months ago

Custom tracking category feature complete

  • Property mode set to 100644
File size: 3.0 KB
Line 
1import React, { useEffect, useMemo, useState } from "react";
2import { createPortal } from "react-dom";
3
4export default function AddCustomCategoryModal({ open, onClose, onSubmit }) {
5 const [name, setName] = useState("");
6 const [submitting, setSubmitting] = useState(false);
7 const [error, setError] = useState(null);
8
9 // Reset the form whenever the modal is opened.
10 // NOTE: Hooks must be called in the same order on every render; do not early-return before hooks.
11 useEffect(() => {
12 if (!open) return;
13 setName("");
14 setError(null);
15 setSubmitting(false);
16 }, [open]);
17
18 const canSubmit = useMemo(() => {
19 return name.trim().length > 0 && name.trim().length <= 100 && !submitting;
20 }, [name, submitting]);
21
22 async function handleSubmit(e) {
23 e.preventDefault();
24 if (!canSubmit) return;
25
26 setSubmitting(true);
27 setError(null);
28 try {
29 await onSubmit({ name: name.trim() });
30 onClose?.();
31 } catch (err) {
32 setError(
33 err?.response?.data?.message ||
34 err?.message ||
35 "Failed to create custom category"
36 );
37 setSubmitting(false);
38 }
39 }
40
41 if (!open) return null;
42
43 return createPortal(
44 <div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
45 <button
46 type="button"
47 className="absolute inset-0 bg-black/60"
48 aria-label="Close modal"
49 onClick={onClose}
50 />
51
52 <div className="relative w-full max-w-md rounded-xl bg-neutral-900 p-5 text-white shadow-xl">
53 <div className="flex items-center justify-between gap-4">
54 <h2 className="text-lg font-semibold">Add Custom Category</h2>
55 <button
56 type="button"
57 onClick={onClose}
58 className="rounded-md px-2 py-1 text-sm text-white/80 hover:bg-white/10"
59 >
60 Close
61 </button>
62 </div>
63
64 <form onSubmit={handleSubmit} className="mt-4 space-y-3">
65 <div>
66 <label className="mb-1 block text-sm text-white/80">Name</label>
67 <input
68 value={name}
69 onChange={(e) => setName(e.target.value)}
70 maxLength={100}
71 autoFocus
72 className="w-full rounded-lg border border-white/10 bg-black/40 px-3 py-2 text-sm outline-none focus:border-green-400"
73 placeholder="e.g. My Habit Tracking"
74 />
75 <div className="mt-1 text-xs text-white/50">
76 {name.trim().length}/100
77 </div>
78 </div>
79
80 {error ? (
81 <div className="rounded-lg border border-red-500/30 bg-red-500/10 p-2 text-sm text-red-200">
82 {error}
83 </div>
84 ) : null}
85
86 <button
87 type="submit"
88 disabled={!canSubmit}
89 className="w-full rounded-lg bg-green-400 px-3 py-2 text-sm font-medium text-black disabled:cursor-not-allowed disabled:opacity-60"
90 >
91 {submitting ? "Creating..." : "Create"}
92 </button>
93 </form>
94 </div>
95 </div>,
96 document.body
97 );
98}
99
100
101
Note: See TracBrowser for help on using the repository browser.