Skip to content

Commit 9b84736

Browse files
authored
Merge pull request #156 from amaydixit11/main
refactored positions and PORs into separate hooks and componentns
2 parents 0043c07 + 2a4ad54 commit 9b84736

9 files changed

Lines changed: 835 additions & 1490 deletions

File tree

frontend/src/Components/Positions/AddPositionForm.jsx

Lines changed: 52 additions & 489 deletions
Large diffs are not rendered by default.

frontend/src/Components/Positions/AddPositionHolderForm.jsx

Lines changed: 102 additions & 509 deletions
Large diffs are not rendered by default.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
2+
import "react-tabs/style/react-tabs.css";
3+
import {
4+
Plus,
5+
X,
6+
AlertCircle,
7+
Search,
8+
Filter,
9+
Eye,
10+
Edit,
11+
Trash2,
12+
Users,
13+
UserCheck,
14+
Award,
15+
Clock,
16+
DollarSign,
17+
MapPin,
18+
Calendar,
19+
} from "lucide-react";
20+
21+
export const ErrorMessage = ({ message }) =>
22+
message ? (
23+
<div className="flex items-center mt-1 text-xs text-red-600">
24+
<AlertCircle className="w-4 h-4 mr-1 flex-shrink-0" />
25+
{message}
26+
</div>
27+
) : null;
28+
29+
export const SearchInput = ({ value, onChange, placeholder }) => (
30+
<div className="flex-1 relative">
31+
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-black w-4 h-4" />
32+
<input
33+
type="text"
34+
placeholder={placeholder}
35+
value={value}
36+
onChange={(e) => onChange(e.target.value)}
37+
className="w-full pl-10 pr-4 py-2 bg-white text-black placeholder-black border border-[#DCD3C9] rounded-lg focus:ring-2 focus:ring-black focus:border-black outline-none"
38+
/>
39+
</div>
40+
);
41+
42+
export const FormInput = ({ label, required, error, ...props }) => (
43+
<div>
44+
<label className="text-sm font-medium text-stone-600">
45+
{label} {required && <span className="text-red-500">*</span>}
46+
</label>
47+
<input
48+
{...props}
49+
className="w-full p-2 mt-1 bg-white border border-stone-300 rounded-lg text-sm text-stone-900 placeholder-stone-400 focus:ring-1 focus:ring-stone-400 focus:border-stone-400 transition"
50+
/>
51+
<ErrorMessage message={error} />
52+
</div>
53+
);
54+
55+
export const FormSelect = ({ label, required, error, options, ...props }) => (
56+
<div>
57+
<label className="text-sm font-medium text-stone-600">
58+
{label} {required && <span className="text-red-500">*</span>}
59+
</label>
60+
<select
61+
{...props}
62+
className="w-full p-2 mt-1 bg-white border border-stone-300 rounded-lg text-sm text-stone-900 focus:ring-1 focus:ring-stone-400 focus:border-stone-400 transition"
63+
>
64+
{options.map((opt) => (
65+
<option key={opt.value} value={opt.value}>
66+
{opt.label}
67+
</option>
68+
))}
69+
</select>
70+
<ErrorMessage message={error} />
71+
</div>
72+
);
73+
74+
export const DynamicFieldArray = ({
75+
label,
76+
array,
77+
onUpdate,
78+
onRemove,
79+
onAdd,
80+
error,
81+
}) => (
82+
<div>
83+
<label className="text-sm font-medium text-stone-600">
84+
{label} <span className="text-red-500">*</span>
85+
</label>
86+
{array.map((item, index) => (
87+
<div key={index} className="flex items-center gap-2 mt-1">
88+
<input
89+
type="text"
90+
value={item}
91+
onChange={(e) => onUpdate(index, e.target.value)}
92+
className="w-full p-2 bg-white border border-stone-300 rounded-lg text-sm text-stone-900 placeholder-stone-400 focus:ring-1 focus:ring-stone-400 focus:border-stone-400 transition flex-grow"
93+
placeholder={`${label.slice(0, -1)} #${index + 1}`}
94+
/>
95+
{array.length > 1 && (
96+
<button
97+
type="button"
98+
onClick={() => onRemove(index)}
99+
className="p-2 text-stone-400 hover:text-red-500 rounded-full hover:bg-red-50 transition-colors"
100+
>
101+
<X size={16} />
102+
</button>
103+
)}
104+
</div>
105+
))}
106+
<button
107+
type="button"
108+
onClick={onAdd}
109+
className="text-stone-600 hover:text-stone-800 font-medium text-sm mt-2 transition flex items-center gap-1"
110+
>
111+
<Plus size={14} /> Add {label.slice(0, -1)}
112+
</button>
113+
<ErrorMessage message={error} />
114+
</div>
115+
);
116+
117+
export const PositionCard = ({ position, onViewDetails, onEdit, onDelete }) => (
118+
<div className="bg-white rounded-lg border-2 border-black hover:shadow-md transition-shadow flex flex-col justify-between">
119+
<div className="p-4">
120+
<div className="flex justify-between items-start mb-2">
121+
<h4 className="font-semibold text-black pr-2">{position.title}</h4>
122+
<span className="flex-shrink-0 px-2 py-1 bg-[#EAE0D5] text-[#856A5D] text-xs rounded-full">
123+
{position.position_type}
124+
</span>
125+
</div>
126+
<div className="flex items-center gap-2 text-sm text-black mb-2">
127+
<MapPin className="w-4 h-4 flex-shrink-0" />
128+
<span>{position.unit_id?.name}</span>
129+
</div>
130+
<div className="flex items-center gap-2 text-sm text-black">
131+
<Users className="w-4 h-4 flex-shrink-0" />
132+
<span>
133+
{position.position_count} position
134+
{position.position_count !== 1 ? "s" : ""}
135+
</span>
136+
</div>
137+
</div>
138+
<div className="p-4 border-t border-[#DCD3C9]">
139+
<div className="flex gap-2">
140+
<button
141+
onClick={() => onViewDetails(position)}
142+
className="flex-1 px-1 py-2 bg-black text-white text-sm rounded-lg hover:bg-[#856A5D] transition-colors flex items-center justify-center gap-1"
143+
>
144+
<Eye className="w-4 h-4" />
145+
View Details
146+
</button>
147+
<button
148+
onClick={() => onEdit(position)}
149+
className="px-3 py-2 bg-[#F5F1EC] text-black text-sm rounded-lg hover:bg-[#EAE0D5] transition-colors"
150+
>
151+
<Edit className="w-4 h-4" />
152+
</button>
153+
<button
154+
onClick={() => onDelete(position)}
155+
className="px-3 py-2 bg-red-100 text-red-700 text-sm rounded-lg hover:bg-red-200 transition-colors"
156+
>
157+
<Trash2 className="w-4 h-4" />
158+
</button>
159+
</div>
160+
</div>
161+
</div>
162+
);

frontend/src/Components/Positions/TenureRecords.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const TenureRecords = () => {
2323
onClick={() => setAdd(!add)}
2424
className="flex items-center gap-2 bg-black text-white text-sm px-4 py-2 rounded-lg hover:bg-[#856A5D] transition-colors"
2525
>
26-
{add ? (
26+
{!add ? (
2727
<div className="flex gap-2">
2828
<Plus className="w-6 h-6" /> <span>Add Position</span>
2929
</div>
@@ -65,13 +65,13 @@ export const CreateTenure = () => {
6565
// ViewTenure: for viewing positions and holders
6666
export const ViewTenure = () => {
6767
return (
68-
<div className="bg-white min-h-screen text-black">
68+
<div className="bg-white text-black">
6969
<Tabs>
7070
<TabList className="flex border-b-2 border-gray-200">
71-
<Tab className="py-2 px-4 font-medium cursor-pointer border-b-3 border-transparent focus:outline-none [&.react-tabs__tab--selected]:border-gray-400">
71+
<Tab className="py-2 px-2 font-medium cursor-pointer border-b-3 border-transparent focus:outline-none [&.react-tabs__tab--selected]:border-gray-400">
7272
View Positions
7373
</Tab>
74-
<Tab className="py-2 px-4 font-medium cursor-pointer border-b-3 border-transparent focus:outline-none [&.react-tabs__tab--selected]:border-gray-400">
74+
<Tab className="py-2 px-2 font-medium cursor-pointer border-b-3 border-transparent focus:outline-none [&.react-tabs__tab--selected]:border-gray-400">
7575
View Position Holder (POR)
7676
</Tab>
7777
</TabList>

0 commit comments

Comments
 (0)