diff --git a/apps/hackathon/src/app/teams/[teamId]/layout.tsx b/apps/hackathon/src/app/teams/[teamId]/layout.tsx index b8cc70d..070436d 100644 --- a/apps/hackathon/src/app/teams/[teamId]/layout.tsx +++ b/apps/hackathon/src/app/teams/[teamId]/layout.tsx @@ -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 = () => { return ( -
- - +
+ setSidebarOpen(false)} /> + +
+ {/* Mobile Header with Hamburger */} +
+ +

🏆 Hackathon

+
+ +
+ +
+
); }; -export default TeamLayout; +export default TeamDetailLayout; diff --git a/apps/hackathon/src/app/teams/[teamId]/page.tsx b/apps/hackathon/src/app/teams/[teamId]/page.tsx index a68b918..2ce921c 100644 --- a/apps/hackathon/src/app/teams/[teamId]/page.tsx +++ b/apps/hackathon/src/app/teams/[teamId]/page.tsx @@ -317,9 +317,6 @@ const TeamDashboardPage: FC = (): ReactElement => {
- - - diff --git a/apps/hackathon/src/app/teams/browse/page.tsx b/apps/hackathon/src/app/teams/browse/page.tsx index 3bca1b3..f04c954 100644 --- a/apps/hackathon/src/app/teams/browse/page.tsx +++ b/apps/hackathon/src/app/teams/browse/page.tsx @@ -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(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 => { - 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" />
- + onChange={setSelectedCity} + placeholder="All Cities (search to filter...)" + />
diff --git a/apps/hackathon/src/app/users/[userId]/layout.tsx b/apps/hackathon/src/app/users/[userId]/layout.tsx index 5b0309d..6ffa5f1 100644 --- a/apps/hackathon/src/app/users/[userId]/layout.tsx +++ b/apps/hackathon/src/app/users/[userId]/layout.tsx @@ -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 = () => { return ( -
- - +
+ setSidebarOpen(false)} /> + +
+ {/* Mobile Header with Hamburger */} +
+ +

🏆 Hackathon

+
+ +
+ +
+
); }; -export default UserLayout; +export default UserDetailLayout; diff --git a/apps/hackathon/src/components/city-select.tsx b/apps/hackathon/src/components/city-select.tsx index 5af998a..a7dc901 100644 --- a/apps/hackathon/src/components/city-select.tsx +++ b/apps/hackathon/src/components/city-select.tsx @@ -70,7 +70,7 @@ export const CitySelect: FC = ({ }} 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' }`} /> diff --git a/libs/service/src/constants/cities.ts b/libs/service/src/constants/cities.ts new file mode 100644 index 0000000..e2ce973 --- /dev/null +++ b/libs/service/src/constants/cities.ts @@ -0,0 +1,522 @@ +export const INDONESIAN_CITIES: string[] = [ + 'Aceh Selatan', + 'Aceh Tenggara', + 'Aceh Timur', + 'Aceh Tengah', + 'Aceh Barat', + 'Aceh Besar', + 'Pidie', + 'Aceh Utara', + 'Simeulue', + 'Aceh Singkil', + 'Bireuen', + 'Aceh Barat Daya', + 'Gayo Lues', + 'Aceh Jaya', + 'Nagan Raya', + 'Aceh Tamiang', + 'Bener Meriah', + 'Pidie Jaya', + 'Kota Banda Aceh', + 'Kota Sabang', + 'Kota Lhokseumawe', + 'Kota Langsa', + 'Kota Subulussalam', + 'Tapanuli Tengah', + 'Tapanuli Utara', + 'Tapanuli Selatan', + 'Nias', + 'Langkat', + 'Karo', + 'Deli Serdang', + 'Simalungun', + 'Asahan', + 'Labuhanbatu', + 'Dairi', + 'Toba', + 'Mandailing Natal', + 'Nias Selatan', + 'Pakpak Bharat', + 'Humbang Hasundutan', + 'Samosir', + 'Serdang Bedagai', + 'Batu Bara', + 'Padang Lawas Utara', + 'Padang Lawas', + 'Labuhanbatu Selatan', + 'Labuhanbatu Utara', + 'Nias Utara', + 'Nias Barat', + 'Kota Medan', + 'Kota Pematangsiantar', + 'Kota Sibolga', + 'Kota Tanjung Balai', + 'Kota Binjai', + 'Kota Tebing Tinggi', + 'Kota Padangsidimpuan', + 'Kota Gunungsitoli', + 'Pesisir Selatan', + 'Solok', + 'Sijunjung', + 'Tanah Datar', + 'Padang Pariaman', + 'Agam', + 'Lima Puluh Kota', + 'Pasaman', + 'Kepulauan Mentawai', + 'Dharmasraya', + 'Solok Selatan', + 'Pasaman Barat', + 'Kota Padang', + 'Kota Solok', + 'Kota Sawahlunto', + 'Kota Padang Panjang', + 'Kota Bukittinggi', + 'Kota Payakumbuh', + 'Kota Pariaman', + 'Kampar', + 'Indragiri Hulu', + 'Bengkalis', + 'Indragiri Hilir', + 'Pelalawan', + 'Rokan Hulu', + 'Rokan Hilir', + 'Siak', + 'Kuantan Singingi', + 'Kepulauan Meranti', + 'Kota Pekanbaru', + 'Kota Dumai', + 'Kerinci', + 'Merangin', + 'Sarolangun', + 'Batanghari', + 'Muaro Jambi', + 'Tanjung Jabung Barat', + 'Tanjung Jabung Timur', + 'Bungo', + 'Tebo', + 'Kota Jambi', + 'Kota Sungai Penuh', + 'Ogan Komering Ulu', + 'Ogan Komering Ilir', + 'Muara Enim', + 'Lahat', + 'Musi Rawas', + 'Musi Banyuasin', + 'Banyuasin', + 'Ogan Komering Ulu Timur', + 'Ogan Komering Ulu Selatan', + 'Ogan Ilir', + 'Empat Lawang', + 'Penukal Abab Lematang Ilir', + 'Musi Rawas Utara', + 'Kota Palembang', + 'Kota Pagar Alam', + 'Kota Lubuk Linggau', + 'Kota Prabumulih', + 'Bengkulu Selatan', + 'Rejang Lebong', + 'Bengkulu Utara', + 'Kaur', + 'Seluma', + 'Muko Muko', + 'Lebong', + 'Kepahiang', + 'Bengkulu Tengah', + 'Kota Bengkulu', + 'Lampung Selatan', + 'Lampung Tengah', + 'Lampung Utara', + 'Lampung Barat', + 'Tulang Bawang', + 'Tanggamus', + 'Lampung Timur', + 'Way Kanan', + 'Pesawaran', + 'Pringsewu', + 'Mesuji', + 'Tulang Bawang Barat', + 'Pesisir Barat', + 'Kota Bandar Lampung', + 'Kota Metro', + 'Bangka', + 'Belitung', + 'Bangka Selatan', + 'Bangka Tengah', + 'Bangka Barat', + 'Belitung Timur', + 'Kota Pangkal Pinang', + 'Bintan', + 'Karimun', + 'Natuna', + 'Lingga', + 'Kepulauan Anambas', + 'Kota Batam', + 'Kota Tanjung Pinang', + 'Kepulauan Seribu', + 'Kota Jakarta Pusat', + 'Kota Jakarta Utara', + 'Kota Jakarta Barat', + 'Kota Jakarta Selatan', + 'Kota Jakarta Timur', + 'Bogor', + 'Sukabumi', + 'Cianjur', + 'Bandung', + 'Garut', + 'Tasikmalaya', + 'Ciamis', + 'Kuningan', + 'Cirebon', + 'Majalengka', + 'Sumedang', + 'Indramayu', + 'Subang', + 'Purwakarta', + 'Karawang', + 'Bekasi', + 'Bandung Barat', + 'Pangandaran', + 'Kota Bogor', + 'Kota Sukabumi', + 'Kota Bandung', + 'Kota Cirebon', + 'Kota Bekasi', + 'Kota Depok', + 'Kota Cimahi', + 'Kota Tasikmalaya', + 'Kota Banjar', + 'Cilacap', + 'Banyumas', + 'Purbalingga', + 'Banjarnegara', + 'Kebumen', + 'Purworejo', + 'Wonosobo', + 'Magelang', + 'Boyolali', + 'Klaten', + 'Sukoharjo', + 'Wonogiri', + 'Karanganyar', + 'Sragen', + 'Grobogan', + 'Blora', + 'Rembang', + 'Pati', + 'Kudus', + 'Jepara', + 'Demak', + 'Semarang', + 'Temanggung', + 'Kendal', + 'Batang', + 'Pekalongan', + 'Pemalang', + 'Tegal', + 'Brebes', + 'Kota Magelang', + 'Kota Surakarta', + 'Kota Salatiga', + 'Kota Semarang', + 'Kota Pekalongan', + 'Kota Tegal', + 'Kulon Progo', + 'Bantul', + 'Gunungkidul', + 'Sleman', + 'Kota Yogyakarta', + 'Pacitan', + 'Ponorogo', + 'Trenggalek', + 'Tulungagung', + 'Blitar', + 'Kediri', + 'Malang', + 'Lumajang', + 'Jember', + 'Banyuwangi', + 'Bondowoso', + 'Situbondo', + 'Probolinggo', + 'Pasuruan', + 'Sidoarjo', + 'Mojokerto', + 'Jombang', + 'Nganjuk', + 'Madiun', + 'Magetan', + 'Ngawi', + 'Bojonegoro', + 'Tuban', + 'Lamongan', + 'Gresik', + 'Bangkalan', + 'Sampang', + 'Pamekasan', + 'Sumenep', + 'Kota Kediri', + 'Kota Blitar', + 'Kota Malang', + 'Kota Probolinggo', + 'Kota Pasuruan', + 'Kota Mojokerto', + 'Kota Madiun', + 'Kota Surabaya', + 'Kota Batu', + 'Pandeglang', + 'Lebak', + 'Tangerang', + 'Serang', + 'Kota Tangerang', + 'Kota Cilegon', + 'Kota Serang', + 'Kota Tangerang Selatan', + 'Jembrana', + 'Tabanan', + 'Badung', + 'Gianyar', + 'Klungkung', + 'Bangli', + 'Karangasem', + 'Buleleng', + 'Kota Denpasar', + 'Lombok Barat', + 'Lombok Tengah', + 'Lombok Timur', + 'Sumbawa', + 'Dompu', + 'Bima', + 'Sumbawa Barat', + 'Lombok Utara', + 'Kota Mataram', + 'Kota Bima', + 'Kupang', + 'Timor Tengah Selatan', + 'Timor Tengah Utara', + 'Belu', + 'Alor', + 'Flores Timur', + 'Sikka', + 'Ende', + 'Ngada', + 'Manggarai', + 'Sumba Timur', + 'Sumba Barat', + 'Lembata', + 'Rote Ndao', + 'Manggarai Barat', + 'Nagekeo', + 'Sumba Tengah', + 'Sumba Barat Daya', + 'Manggarai Timur', + 'Sabu Raijua', + 'Malaka', + 'Kota Kupang', + 'Sambas', + 'Mempawah', + 'Sanggau', + 'Ketapang', + 'Sintang', + 'Kapuas Hulu', + 'Bengkayang', + 'Landak', + 'Sekadau', + 'Melawi', + 'Kayong Utara', + 'Kubu Raya', + 'Kota Pontianak', + 'Kota Singkawang', + 'Kotawaringin Barat', + 'Kotawaringin Timur', + 'Kapuas', + 'Barito Selatan', + 'Barito Utara', + 'Katingan', + 'Seruyan', + 'Sukamara', + 'Lamandau', + 'Gunung Mas', + 'Pulang Pisau', + 'Murung Raya', + 'Barito Timur', + 'Kota Palangkaraya', + 'Tanah Laut', + 'Kotabaru', + 'Banjar', + 'Barito Kuala', + 'Tapin', + 'Hulu Sungai Selatan', + 'Hulu Sungai Tengah', + 'Hulu Sungai Utara', + 'Tabalong', + 'Tanah Bumbu', + 'Balangan', + 'Kota Banjarmasin', + 'Kota Banjarbaru', + 'Paser', + 'Kutai Kartanegara', + 'Berau', + 'Kutai Barat', + 'Kutai Timur', + 'Penajam Paser Utara', + 'Mahakam Ulu', + 'Kota Balikpapan', + 'Kota Samarinda', + 'Kota Bontang', + 'Bulungan', + 'Malinau', + 'Nunukan', + 'Tana Tidung', + 'Kota Tarakan', + 'Bolaang Mongondow', + 'Minahasa', + 'Kepulauan Sangihe', + 'Kepulauan Talaud', + 'Minahasa Selatan', + 'Minahasa Utara', + 'Minahasa Tenggara', + 'Bolaang Mongondow Utara', + 'Kepulauan Siau Tagulandang Biaro (Sitaro)', + 'Bolaang Mongondow Timur', + 'Bolaang Mongondow Selatan', + 'Kota Manado', + 'Kota Bitung', + 'Kota Tomohon', + 'Kota Kotamobagu', + 'Banggai', + 'Poso', + 'Donggala', + 'Toli Toli', + 'Buol', + 'Morowali', + 'Banggai Kepulauan', + 'Parigi Moutong', + 'Tojo Una Una', + 'Sigi', + 'Banggai Laut', + 'Morowali Utara', + 'Kota Palu', + 'Kepulauan Selayar', + 'Bulukumba', + 'Bantaeng', + 'Jeneponto', + 'Takalar', + 'Gowa', + 'Sinjai', + 'Bone', + 'Maros', + 'Pangkajene Kepulauan', + 'Barru', + 'Soppeng', + 'Wajo', + 'Sidenreng Rappang', + 'Pinrang', + 'Enrekang', + 'Luwu', + 'Tana Toraja', + 'Luwu Utara', + 'Luwu Timur', + 'Toraja Utara', + 'Kota Makassar', + 'Kota Pare Pare', + 'Kota Palopo', + 'Kolaka', + 'Konawe', + 'Muna', + 'Buton', + 'Konawe Selatan', + 'Bombana', + 'Wakatobi', + 'Kolaka Utara', + 'Konawe Utara', + 'Buton Utara', + 'Kolaka Timur', + 'Konawe Kepulauan', + 'Muna Barat', + 'Buton Tengah', + 'Buton Selatan', + 'Kota Kendari', + 'Kota Bau Bau', + 'Gorontalo', + 'Boalemo', + 'Bone Bolango', + 'Pahuwato', + 'Gorontalo Utara', + 'Kota Gorontalo', + 'Pasangkayu (Mamuju Utara)', + 'Mamuju', + 'Mamasa', + 'Polewali Mandar', + 'Majene', + 'Mamuju Tengah', + 'Maluku Tengah', + 'Maluku Tenggara', + 'Kepulauan Tanimbar (Maluku Tenggara Barat)', + 'Buru', + 'Seram Bagian Timur', + 'Seram Bagian Barat', + 'Kepulauan Aru', + 'Maluku Barat Daya', + 'Buru Selatan', + 'Kota Ambon', + 'Kota Tual', + 'Halmahera Barat', + 'Halmahera Tengah', + 'Halmahera Utara', + 'Halmahera Selatan', + 'Kepulauan Sula', + 'Halmahera Timur', + 'Pulau Morotai', + 'Pulau Taliabu', + 'Kota Ternate', + 'Kota Tidore Kepulauan', + 'Jayapura', + 'Kepulauan Yapen', + 'Biak Numfor', + 'Sarmi', + 'Keerom', + 'Waropen', + 'Supiori', + 'Mamberamo Raya', + 'Kota Jayapura', + 'Manokwari', + 'Fak Fak', + 'Teluk Bintuni', + 'Teluk Wondama', + 'Kaimana', + 'Manokwari Selatan', + 'Pegunungan Arfak', + 'Merauke', + 'Boven Digoel', + 'Mappi', + 'Asmat', + 'Nabire', + 'Puncak Jaya', + 'Paniai', + 'Mimika', + 'Puncak', + 'Dogiyai', + 'Intan Jaya', + 'Deiyai', + 'Jayawijaya', + 'Pegunungan Bintang', + 'Yahukimo', + 'Tolikara', + 'Mamberamo Tengah', + 'Yalimo', + 'Lanny Jaya', + 'Nduga', + 'Sorong', + 'Sorong Selatan', + 'Raja Ampat', + 'Tambrauw', + 'Maybrat', + 'Kota Sorong', +]; + +export const INDONESIAN_CITIES_SET = new Set(INDONESIAN_CITIES); + +export function isValidIndonesianCity(city: string): boolean { + return INDONESIAN_CITIES_SET.has(city); +}