Browse Source

feat: implement CustomTripOriginsTab component with multi-language and country management support

master
Muhammad A. Ghorbani 2 weeks ago
parent
commit
ed5ae5f774
  1. 2
      src/features/custom-trip/components/CustomTripDestinationsTab.tsx
  2. 20
      src/features/custom-trip/components/CustomTripHotelsTab.tsx
  3. 68
      src/features/custom-trip/components/CustomTripOriginsTab.tsx
  4. 1431
      src/features/custom-trip/services/custom-trip-api.ts

2
src/features/custom-trip/components/CustomTripDestinationsTab.tsx

@ -585,7 +585,7 @@ export function CustomTripDestinationsTab({
</div>
{/* Country filter chips */}
<div className="flex items-center gap-1.5 overflow-x-auto pt-1 pb-0.5">
<div className="flex flex-wrap items-center gap-x-1.5 gap-y-2 pt-1 pb-0.5">
<span className="text-xs text-muted-foreground font-medium shrink-0 ps-1">
{t('customTrip.filterByCountry')}:
</span>

20
src/features/custom-trip/components/CustomTripHotelsTab.tsx

@ -52,7 +52,7 @@ export function CustomTripHotelsTab({
const { t } = useTranslation()
const [search, setSearch] = useState('')
const [selectedDestination, setSelectedDestination] = useState(initialDestinationFilter)
const [starsFilter, setStarsFilter] = useState<'all' | '5' | '4' | '3'>('all')
const [starsFilter, setStarsFilter] = useState<'all' | '5' | '4' | '3' | '2'>('all')
const [viewMode, setViewMode] = useState<'cards' | 'table'>('cards')
// Modals state
@ -375,6 +375,24 @@ export function CustomTripHotelsTab({
<span className="text-amber-500">★</span>
<span>۴ ستاره</span>
</button>
<button
onClick={() => setStarsFilter('3')}
className={`px-2 py-1 rounded-lg text-xs font-medium transition-colors flex items-center gap-1 ${
starsFilter === '3' ? 'bg-card text-foreground shadow-2xs font-bold' : 'text-muted-foreground'
}`}
>
<span className="text-amber-500">★</span>
<span>۳ ستاره</span>
</button>
<button
onClick={() => setStarsFilter('2')}
className={`px-2 py-1 rounded-lg text-xs font-medium transition-colors flex items-center gap-1 ${
starsFilter === '2' ? 'bg-card text-foreground shadow-2xs font-bold' : 'text-muted-foreground'
}`}
>
<span className="text-amber-500">★</span>
<span>۲ ستاره</span>
</button>
</div>
{/* View Mode Switcher */}

68
src/features/custom-trip/components/CustomTripOriginsTab.tsx

@ -336,6 +336,7 @@ export function CustomTripOriginsTab({
}
itemToDeleteRef.current = null
setItemToDelete(null)
setIsFormOpen(false)
}
const internationalCount = origins.filter((o) => o.isInternational).length
@ -398,7 +399,7 @@ export function CustomTripOriginsTab({
</div>
{/* Country filter chips */}
<div className="flex items-center gap-1.5 overflow-x-auto pt-1 pb-0.5">
<div className="flex flex-wrap items-center gap-x-1.5 gap-y-2 pt-1 pb-0.5">
<span className="text-xs text-muted-foreground font-medium shrink-0 ps-1">
{t('customTrip.filterByCountry')}:
</span>
@ -444,7 +445,7 @@ export function CustomTripOriginsTab({
<th className="px-4 py-3.5 text-start">{t('customTrip.originCityName')}</th>
<th className="px-4 py-3.5 text-start">{t('customTrip.country')}</th>
<th className="px-4 py-3.5 text-center">وضعیت</th>
<th className="px-4 py-3.5 text-center">{t('customTrip.actions')}</th>
<th className="px-4 py-3.5 text-end">{t('customTrip.actions')}</th>
</tr>
</thead>
<tbody className="divide-y divide-border/50">
@ -484,49 +485,37 @@ export function CustomTripOriginsTab({
</span>
</td>
<td className="px-4 py-3.5 text-center">
<button
type="button"
onClick={() => {
<div className="inline-flex items-center justify-center gap-2">
<Switch
checked={item.isActive}
onCheckedChange={(checked) => {
if (onUpdateOrigin) {
onUpdateOrigin({ ...item, isActive: !item.isActive })
onUpdateOrigin({ ...item, isActive: checked })
}
}}
className={`inline-flex items-center gap-1.5 text-xs font-semibold px-2.5 py-1 rounded-full transition-all cursor-pointer ${
/>
<span
className={cn(
'text-xs font-semibold select-none min-w-[42px] text-start',
item.isActive
? 'bg-emerald-500/10 text-emerald-600 hover:bg-emerald-500/20'
: 'bg-muted text-muted-foreground hover:bg-muted/80'
}`}
? 'text-emerald-600 dark:text-emerald-400'
: 'text-muted-foreground'
)}
>
<span
className={`size-1.5 rounded-full ${
item.isActive ? 'bg-emerald-500 animate-pulse' : 'bg-muted-foreground'
}`}
/>
{item.isActive ? t('customTrip.active') : t('customTrip.inactive')}
</button>
</span>
</div>
</td>
<td className="px-4 py-3.5 text-center">
<div className="flex items-center justify-center gap-1">
<td className="px-4 py-3.5 text-end">
<Button
variant="ghost"
size="sm"
onClick={() => handleOpenEdit(item)}
className="size-8 p-0 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground"
title={t('customTrip.editOrigin')}
className="size-8 p-0 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground inline-flex items-center justify-center cursor-pointer"
title={t('common.edit')}
>
<Ic name="pencil" className="size-3.5" />
</Button>
<Button
variant="ghost"
size="sm"
type="button"
onClick={() => handleDeleteClick(item)}
className="size-8 p-0 rounded-lg hover:bg-rose-500/10 text-muted-foreground hover:text-rose-600 cursor-pointer"
title={t('customTrip.deleteOrigin')}
>
<Ic name="trash" className="size-3.5" />
</Button>
</div>
</td>
</tr>
)
@ -743,7 +732,21 @@ export function CustomTripOriginsTab({
</div>
{/* فوتر فیکس‌شده در پایین مدال */}
<DialogFooter className="p-4 sm:p-5 border-t border-border-soft bg-card/95 backdrop-blur-xs flex items-center justify-end gap-2.5 shrink-0 mt-0">
<DialogFooter className="p-4 sm:p-5 border-t border-border-soft bg-card/95 backdrop-blur-xs flex items-center justify-between gap-2.5 shrink-0 mt-0">
{editingItem ? (
<Button
variant="ghost"
type="button"
onClick={() => handleDeleteClick(editingItem)}
className="rounded-xl text-xs h-9 px-3 gap-1.5 text-rose-600 hover:text-rose-700 hover:bg-rose-500/10 border border-rose-200/60 dark:border-rose-900/40 cursor-pointer"
>
<Ic name="trash" className="size-3.5" />
<span>{t('customTrip.deleteOrigin')}</span>
</Button>
) : (
<div />
)}
<div className="flex items-center gap-2">
<Button
variant="outline"
type="button"
@ -758,6 +761,7 @@ export function CustomTripOriginsTab({
>
{t('customTrip.saveChanges')}
</Button>
</div>
</DialogFooter>
</form>
</DialogContent>

1431
src/features/custom-trip/services/custom-trip-api.ts
File diff suppressed because it is too large
View File

Loading…
Cancel
Save