feat(backoffice): FE integration (data fetch) for dashboard, teams, submission, and user management page (#70)
* feat(backoffice): create a boilerplate page for Hackathon dashboard - Create an empty page for Hackathon dashboard - Comment out and hide the existing backoffice sidebar * feat(backoffice): Create a nested/dropdown sidebar list - Create a dropdown sidebar list - Show back the old navigation and group them - Make the sidebar responsive for mobile view * test datatable with mock data * base UI for Hackathon backoffice TODO: - Organize table schema for users, teams, and submissions management - Create API Contract for additional back-end endpoint * feat(hackathon): draft data table column & API Contract * update endpoint * feat(backoffice): update page hackathon user management - update data table component - update filtering & pagination - add modal display to edit and add user - hide notification icon in backoffice wrapper * feat(backoffice): add API contract for hackathon users * feat(backoffice): little adjustment in hackathon users management UI and API contract * feat(backoffice): update hackathon team management page - add modal for manage team, add new team, and view project submission - reorganize the table column and data table * feat(backoffice): add searchable city filter - add component for city filter - apply to user management and team management pages * feat(backoffice): improve hackathon team modal UI and add API contract - Add feature to select city in team detail modal using CityFilterSelect component - Add feature to change team logo and banner - Add API contract documentation for hackathon teams in backoffice * feat(backoffice): authentication middleware, error pages, and 404 page * feat(backoffice): hackathon dashboard integration * feat(backoffice): users page integration - Get users data from API - Set up server-side pagination and match URL params - Hide filter that doesn't exist in back-end * feat(backoffice): teams page integration - Get teams data from API - Hide filter that doesn't exists in back-end - Simplify modal according to the back-end * feat(backoffice): submission page integration - Fetch submissions data from API - Move submission modal to hackathon-submissions page
This commit is contained in:
+14
-10
@@ -15,10 +15,10 @@ import {
|
||||
|
||||
interface UserType {
|
||||
id: string;
|
||||
avatar?: string;
|
||||
avatar?: string | null;
|
||||
fullname: string;
|
||||
bio?: string;
|
||||
location: string;
|
||||
location: string | null;
|
||||
is_active: boolean;
|
||||
skills: string[];
|
||||
created_at: string;
|
||||
@@ -77,7 +77,7 @@ const ModalUserDetail: FC<ModalProps> = ({ isOpen, onClose, user }) => {
|
||||
// Check if required fields are filled
|
||||
const isFormValid = useMemo(() => {
|
||||
if (!formData) return false;
|
||||
return formData.fullname.trim() !== '' && formData.location.trim() !== '';
|
||||
return formData.fullname?.trim() !== '' && formData.location?.trim() !== '';
|
||||
}, [formData]);
|
||||
|
||||
const canSave = hasChanges && isFormValid;
|
||||
@@ -259,13 +259,13 @@ const ModalUserDetail: FC<ModalProps> = ({ isOpen, onClose, user }) => {
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="p-2 hover:bg-neutral-100 rounded-lg transition-colors"
|
||||
className="p-2 hover:bg-neutral-100 rounded-lg transition-colors cursor-pointer"
|
||||
onClick={() => {
|
||||
setShowAvatarMenu(false);
|
||||
handleCancel();
|
||||
}}
|
||||
>
|
||||
<CloseOutlined className="text-neutral-400 text-lg cursor-pointer" />
|
||||
<CloseOutlined className="text-neutral-400 text-lg" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -294,13 +294,15 @@ const ModalUserDetail: FC<ModalProps> = ({ isOpen, onClose, user }) => {
|
||||
}
|
||||
className={cn(
|
||||
'w-full border rounded-lg px-3 py-2 text-sm focus:border-primary-500 focus:outline-none',
|
||||
formData.fullname.trim() === ''
|
||||
!formData.fullname ||
|
||||
formData.fullname.trim() === ''
|
||||
? 'border-red-300 bg-red-50'
|
||||
: 'border-neutral-300'
|
||||
)}
|
||||
placeholder="Enter full name"
|
||||
/>
|
||||
{formData.fullname.trim() === '' && (
|
||||
{(!formData.fullname ||
|
||||
formData.fullname.trim() === '') && (
|
||||
<p className="text-red-500 text-xs mt-1">
|
||||
Full name is required
|
||||
</p>
|
||||
@@ -316,13 +318,14 @@ const ModalUserDetail: FC<ModalProps> = ({ isOpen, onClose, user }) => {
|
||||
Location <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={formData.location}
|
||||
value={formData.location || ''}
|
||||
onChange={(e) =>
|
||||
handleInputChange('location', e.target.value)
|
||||
}
|
||||
className={cn(
|
||||
'w-full border rounded-lg px-3 py-2 text-sm focus:border-primary-500 focus:outline-none bg-white',
|
||||
formData.location.trim() === ''
|
||||
!formData.location ||
|
||||
formData.location.trim() === ''
|
||||
? 'border-red-300 bg-red-50'
|
||||
: 'border-neutral-300'
|
||||
)}
|
||||
@@ -334,7 +337,8 @@ const ModalUserDetail: FC<ModalProps> = ({ isOpen, onClose, user }) => {
|
||||
<option value="Medan">Medan</option>
|
||||
<option value="Yogyakarta">Yogyakarta</option>
|
||||
</select>
|
||||
{formData.location.trim() === '' && (
|
||||
{(!formData.location ||
|
||||
formData.location.trim() === '') && (
|
||||
<p className="text-red-500 text-xs mt-1">
|
||||
Location is required
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user