Merge branch 'develop' into hackathon/fix-ui

This commit is contained in:
Hafid Nur
2025-11-26 14:27:32 +07:00
6 changed files with 599 additions and 47 deletions
@@ -1,18 +1,34 @@
import { FC, ReactNode } from 'react';
import { FC, ReactElement, useState } from 'react';
import { Outlet } from 'react-router';
import { Navigation } from '../../../components/navigation';
import { Sidebar } from '../../../components/sidebar';
interface TeamLayoutProps {
children?: ReactNode;
}
const TeamDetailLayout: FC = (): ReactElement => {
const [sidebarOpen, setSidebarOpen] = useState(false);
export const TeamLayout: FC<TeamLayoutProps> = () => {
return (
<div className="min-h-screen bg-gray-50">
<Navigation />
<Outlet />
<div className="flex min-h-screen bg-gray-50">
<Sidebar isOpen={sidebarOpen} onClose={() => setSidebarOpen(false)} />
<div className="flex-1 flex flex-col overflow-auto">
{/* Mobile Header with Hamburger */}
<div className="lg:hidden bg-white border-b px-4 py-3 flex items-center">
<button
onClick={() => setSidebarOpen(true)}
className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
>
<svg className="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<h1 className="ml-3 text-lg font-bold text-gray-900">🏆 Hackathon</h1>
</div>
<main className="flex-1 overflow-auto">
<Outlet />
</main>
</div>
</div>
);
};
export default TeamLayout;
export default TeamDetailLayout;
@@ -317,9 +317,6 @@ const TeamDashboardPage: FC = (): ReactElement => {
</div>
</div>
</div>
<Link to="/dashboard" className="hidden md:block">
<Button variant="secondary">Back to Dashboard</Button>
</Link>
</div>
</div>
</div>
+24 -23
View File
@@ -1,23 +1,30 @@
import { FC, ReactElement, useState } from 'react';
import { Button, Input, Select } from '@imphnen-frontend-service/ui/atoms';
import { FC, ReactElement, useState, useEffect } from 'react';
import { Button } from '@imphnen-frontend-service/ui/atoms';
import { Link, useNavigate } from 'react-router';
import { useTeams, useJoinTeam, useMyTeams, ETeamVisibility, joinTeamSchema, TJoinTeamForm } from '@imphnen-frontend-service/service';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import INDONESIAN_CITIES_DATA from '../../../constants/cities';
const INDONESIAN_CITIES = ['All Cities', ...INDONESIAN_CITIES_DATA];
import { CitySelect } from '../../../components/city-select';
const BrowseTeamsPage: FC = (): ReactElement => {
const navigate = useNavigate();
const [search, setSearch] = useState('');
const [selectedCity, setSelectedCity] = useState('All Cities');
const [searchTerm, setSearchTerm] = useState('');
const [debouncedSearch, setDebouncedSearch] = useState('');
const [selectedCity, setSelectedCity] = useState('');
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
const [showJoinModal, setShowJoinModal] = useState(false);
// Debounce search term
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedSearch(searchTerm);
}, 300);
return () => clearTimeout(timer);
}, [searchTerm]);
const { data: teamsData, isLoading } = useTeams({
search,
city: selectedCity === 'All Cities' ? undefined : selectedCity,
search: debouncedSearch,
city: selectedCity || undefined,
visibility: ETeamVisibility.PUBLIC,
});
@@ -80,29 +87,23 @@ const BrowseTeamsPage: FC = (): ReactElement => {
<label className="block text-sm font-medium text-gray-700 mb-2">
Search Teams
</label>
<Input
<input
type="text"
placeholder="Search by team name..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="h-12"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full h-[42px] px-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Filter by City
</label>
<Select
<CitySelect
value={selectedCity}
onChange={(e) => setSelectedCity(e.target.value)}
className="w-full"
>
{INDONESIAN_CITIES.map((city) => (
<option key={city} value={city}>
{city}
</option>
))}
</Select>
onChange={setSelectedCity}
placeholder="All Cities (search to filter...)"
/>
</div>
</div>
</div>
@@ -1,18 +1,34 @@
import { FC, ReactNode } from 'react';
import { FC, ReactElement, useState } from 'react';
import { Outlet } from 'react-router';
import { Navigation } from '../../../components/navigation';
import { Sidebar } from '../../../components/sidebar';
interface UserLayoutProps {
children?: ReactNode;
}
const UserDetailLayout: FC = (): ReactElement => {
const [sidebarOpen, setSidebarOpen] = useState(false);
export const UserLayout: FC<UserLayoutProps> = () => {
return (
<div className="min-h-screen bg-gray-50">
<Navigation />
<Outlet />
<div className="flex min-h-screen bg-gray-50">
<Sidebar isOpen={sidebarOpen} onClose={() => setSidebarOpen(false)} />
<div className="flex-1 flex flex-col overflow-auto">
{/* Mobile Header with Hamburger */}
<div className="lg:hidden bg-white border-b px-4 py-3 flex items-center">
<button
onClick={() => setSidebarOpen(true)}
className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
>
<svg className="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<h1 className="ml-3 text-lg font-bold text-gray-900">🏆 Hackathon</h1>
</div>
<main className="flex-1 overflow-auto">
<Outlet />
</main>
</div>
</div>
);
};
export default UserLayout;
export default UserDetailLayout;
@@ -70,7 +70,7 @@ export const CitySelect: FC<CitySelectProps> = ({
}}
onClick={handleInputClick}
placeholder={placeholder}
className={`w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
className={`w-full h-[42px] px-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
error ? 'border-red-500' : 'border-gray-300'
}`}
/>