fix ui style
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { CircleCheck } from "lucide-react";
|
||||
import { adminCancelTripAction } from "@/features/trip/actions";
|
||||
|
||||
interface AdminCancelTripButtonProps {
|
||||
@@ -42,7 +43,10 @@ export function AdminCancelTripButton({ tripId }: AdminCancelTripButtonProps) {
|
||||
if (result) {
|
||||
return (
|
||||
<div className="rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-900">
|
||||
<p className="font-bold">✅ Trip berhasil dibatalkan.</p>
|
||||
<p className="flex items-center gap-1.5 font-bold">
|
||||
<CircleCheck size={18} strokeWidth={2} aria-hidden />
|
||||
Trip berhasil dibatalkan.
|
||||
</p>
|
||||
<ul className="mt-2 space-y-0.5 text-xs">
|
||||
<li>• {result.refundCount} booking PAID → refund auto-dibuat</li>
|
||||
<li>
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
import { useMemo, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
Check,
|
||||
X,
|
||||
CircleAlert,
|
||||
Users,
|
||||
} from "lucide-react";
|
||||
import { DateRangeField, TimeField } from "@/components/shared/date-picker";
|
||||
import { createTripAction } from "@/features/trip/actions";
|
||||
import { ImageUrlInput } from "@/features/trip/components/image-url-input";
|
||||
@@ -367,9 +375,10 @@ export function CreateTripForm({ isVerifiedOrganizer }: CreateTripFormProps) {
|
||||
type="button"
|
||||
onClick={goBack}
|
||||
disabled={step === 1 || loading}
|
||||
className="rounded-xl border border-neutral-200 bg-white px-4 py-2.5 text-sm font-semibold text-neutral-700 transition-colors hover:bg-neutral-50 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
className="inline-flex items-center gap-1 rounded-xl border border-neutral-200 bg-white px-4 py-2.5 text-sm font-semibold text-neutral-700 transition-colors hover:bg-neutral-50 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
← Kembali
|
||||
<ArrowLeft size={15} strokeWidth={2} aria-hidden />
|
||||
Kembali
|
||||
</button>
|
||||
|
||||
{isLastStep ? (
|
||||
@@ -388,9 +397,10 @@ export function CreateTripForm({ isVerifiedOrganizer }: CreateTripFormProps) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={goNext}
|
||||
className="rounded-xl bg-primary-600 px-5 py-2.5 text-sm font-bold text-white shadow-lg shadow-primary-600/20 transition-colors hover:bg-primary-700"
|
||||
className="inline-flex items-center gap-1 rounded-xl bg-primary-600 px-5 py-2.5 text-sm font-bold text-white shadow-lg shadow-primary-600/20 transition-colors hover:bg-primary-700"
|
||||
>
|
||||
Lanjut →
|
||||
Lanjut
|
||||
<ArrowRight size={15} strokeWidth={2} aria-hidden />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -442,7 +452,11 @@ function Stepper({
|
||||
: "cursor-not-allowed"
|
||||
}`}
|
||||
>
|
||||
{isCompleted ? "✓" : s.id}
|
||||
{isCompleted ? (
|
||||
<Check size={14} strokeWidth={3} aria-hidden />
|
||||
) : (
|
||||
s.id
|
||||
)}
|
||||
</button>
|
||||
<span
|
||||
className={`ml-2 hidden text-xs font-semibold sm:inline ${
|
||||
@@ -942,7 +956,7 @@ function ItineraryBuilder({
|
||||
aria-label="Hapus aktivitas"
|
||||
className="self-end rounded-lg px-2 py-1.5 text-neutral-400 hover:bg-red-50 hover:text-red-500 sm:self-center"
|
||||
>
|
||||
✕
|
||||
<X size={16} strokeWidth={2} aria-hidden />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
@@ -1032,14 +1046,7 @@ function StepSchedule({
|
||||
</label>
|
||||
<div className="relative">
|
||||
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-neutral-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
className="h-4 w-4"
|
||||
>
|
||||
<path d="M7 8a3 3 0 100-6 3 3 0 000 6zM14.5 9a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM1.615 16.428a1.224 1.224 0 01-.569-1.175 6.002 6.002 0 0111.908 0c.058.467-.172.92-.57 1.174A9.953 9.953 0 017 18a9.953 9.953 0 01-5.385-1.572zM14.5 16h-.106c.07-.297.088-.611.048-.933a7.47 7.47 0 00-1.588-3.755 4.502 4.502 0 015.874 2.636.818.818 0 01-.36.98A7.465 7.465 0 0114.5 16z" />
|
||||
</svg>
|
||||
<Users size={16} strokeWidth={1.75} aria-hidden />
|
||||
</span>
|
||||
<input
|
||||
id="maxParticipants"
|
||||
@@ -1084,8 +1091,9 @@ function StepSchedule({
|
||||
/>
|
||||
</div>
|
||||
{blockedByVerification && (
|
||||
<p className="mt-2 text-xs font-medium text-amber-700">
|
||||
⚠️ Trip berbayar butuh verifikasi organizer terlebih dahulu.
|
||||
<p className="mt-2 flex items-center gap-1.5 text-xs font-medium text-amber-700">
|
||||
<CircleAlert size={14} strokeWidth={2} aria-hidden />
|
||||
Trip berbayar butuh verifikasi organizer terlebih dahulu.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import { Mountain } from "lucide-react";
|
||||
|
||||
interface TripImage {
|
||||
id: string;
|
||||
@@ -14,8 +15,13 @@ export function ImageGallery({ images }: { images: TripImage[] }) {
|
||||
|
||||
if (images.length === 0) {
|
||||
return (
|
||||
<div className="flex h-44 items-center justify-center bg-linear-to-br from-primary-800 to-secondary-900 sm:h-56 lg:h-72">
|
||||
<span className="text-5xl sm:text-6xl">🏔️</span>
|
||||
<div className="flex h-44 items-center justify-center bg-neutral-100 sm:h-56 lg:h-72">
|
||||
<Mountain
|
||||
size={56}
|
||||
strokeWidth={1.5}
|
||||
aria-hidden
|
||||
className="text-neutral-300"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Plus, X } from "lucide-react";
|
||||
import { LIMITS } from "@/lib/limits";
|
||||
|
||||
interface ImageUrlInputProps {
|
||||
@@ -59,7 +60,7 @@ export function ImageUrlInput({ value, onChange }: ImageUrlInputProps) {
|
||||
aria-label={`Hapus foto ${i + 1}`}
|
||||
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-neutral-200 text-neutral-400 hover:bg-red-50 hover:text-red-500"
|
||||
>
|
||||
✕
|
||||
<X size={16} strokeWidth={2} aria-hidden />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -71,7 +72,8 @@ export function ImageUrlInput({ value, onChange }: ImageUrlInputProps) {
|
||||
onClick={addField}
|
||||
className="mt-2 flex items-center gap-1 rounded-lg px-2 py-1 text-sm font-medium text-secondary-600 hover:bg-secondary-50"
|
||||
>
|
||||
+ Tambah foto
|
||||
<Plus size={15} strokeWidth={2} aria-hidden />
|
||||
Tambah foto
|
||||
</button>
|
||||
)}
|
||||
<p className="mt-1.5 text-xs text-neutral-400">
|
||||
|
||||
@@ -143,7 +143,7 @@ export function JoinTripButton({
|
||||
Kamu sudah{" "}
|
||||
<span className="font-semibold">terkonfirmasi</span> sebagai peserta
|
||||
trip ini
|
||||
{isFree && <span> — trip gratis, tidak ada pembayaran 🎉</span>}.
|
||||
{isFree && <span> — trip gratis, tidak ada pembayaran</span>}.
|
||||
</div>
|
||||
)}
|
||||
{needsPayment && (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Image from "next/image";
|
||||
import { BadgeCheck, Star } from "lucide-react";
|
||||
import type { OrganizerTrust } from "@/server/services/trust.service";
|
||||
|
||||
interface OrganizerTrustPanelProps {
|
||||
@@ -13,7 +14,7 @@ export function OrganizerTrustPanel({
|
||||
trust,
|
||||
}: OrganizerTrustPanelProps) {
|
||||
return (
|
||||
<div className="rounded-xl border border-neutral-200 bg-linear-to-br from-white to-neutral-50 p-4 sm:p-5">
|
||||
<div className="rounded-xl border border-neutral-200 bg-white p-4 sm:p-5">
|
||||
<h2 className="mb-3 text-xs font-bold text-neutral-700 sm:text-sm">
|
||||
Organizer & kepercayaan
|
||||
</h2>
|
||||
@@ -42,7 +43,8 @@ export function OrganizerTrustPanel({
|
||||
className="inline-flex items-center gap-1 rounded-full bg-primary-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-primary-800 sm:text-xs"
|
||||
title="Identitas organizer telah diverifikasi (KTP & rekening)"
|
||||
>
|
||||
✅ Verified Organizer
|
||||
<BadgeCheck size={12} strokeWidth={2} aria-hidden />
|
||||
Verified Organizer
|
||||
</span>
|
||||
)}
|
||||
{trust.isTripLeader && (
|
||||
@@ -79,8 +81,20 @@ export function OrganizerTrustPanel({
|
||||
<p className="text-[10px] font-medium text-neutral-500 sm:text-xs">
|
||||
Rating organizer
|
||||
</p>
|
||||
<p className="text-lg font-bold text-amber-700">
|
||||
{trust.avgRating != null ? `${trust.avgRating} ★` : "—"}
|
||||
<p className="flex items-center gap-1 text-lg font-bold text-amber-700">
|
||||
{trust.avgRating != null ? (
|
||||
<>
|
||||
{trust.avgRating}
|
||||
<Star
|
||||
size={15}
|
||||
strokeWidth={2}
|
||||
fill="currentColor"
|
||||
aria-hidden
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
"—"
|
||||
)}
|
||||
</p>
|
||||
{trust.reviewCount > 0 && (
|
||||
<p className="text-[10px] text-neutral-400">
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
MapPin,
|
||||
CalendarDays,
|
||||
UserRound,
|
||||
BadgeCheck,
|
||||
Sparkles,
|
||||
} from "lucide-react";
|
||||
import { formatRupiah } from "@/lib/utils";
|
||||
import { formatTripCalendarDateRangeLong } from "@/lib/trip-dates";
|
||||
import { categoryMeta } from "@/lib/activity-category";
|
||||
@@ -132,21 +139,38 @@ export function TripCard({
|
||||
|
||||
<div className="mt-3 space-y-1 text-sm text-neutral-600">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-xs text-secondary-500">📍</span> {location}
|
||||
<MapPin
|
||||
size={14}
|
||||
strokeWidth={1.75}
|
||||
aria-hidden
|
||||
className="shrink-0 text-neutral-400"
|
||||
/>
|
||||
<span className="truncate">{location}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-xs text-secondary-500">📅</span>{" "}
|
||||
{formatTripCalendarDateRangeLong(date, endDate)}
|
||||
<CalendarDays
|
||||
size={14}
|
||||
strokeWidth={1.75}
|
||||
aria-hidden
|
||||
className="shrink-0 text-neutral-400"
|
||||
/>
|
||||
<span>{formatTripCalendarDateRangeLong(date, endDate)}</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-xs text-secondary-500">👤</span>{" "}
|
||||
<UserRound
|
||||
size={14}
|
||||
strokeWidth={1.75}
|
||||
aria-hidden
|
||||
className="shrink-0 text-neutral-400"
|
||||
/>
|
||||
<span className="truncate">{organizerName}</span>
|
||||
{isVerifiedOrganizer && (
|
||||
<span
|
||||
className="inline-flex items-center gap-0.5 rounded-full bg-primary-100 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide text-primary-800"
|
||||
className="inline-flex items-center gap-1 rounded-full bg-primary-100 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide text-primary-800"
|
||||
title="Identitas organizer telah diverifikasi (KTP & rekening)"
|
||||
>
|
||||
✅ Verified
|
||||
<BadgeCheck size={11} strokeWidth={2} aria-hidden />
|
||||
Verified
|
||||
</span>
|
||||
)}
|
||||
{isSmallGroup && (
|
||||
@@ -193,10 +217,11 @@ export function TripCard({
|
||||
)}
|
||||
{overlapCount > 0 && (
|
||||
<span
|
||||
className="rounded-full bg-secondary-50 px-2 py-0.5 text-[11px] font-semibold text-secondary-700"
|
||||
className="inline-flex items-center gap-1 rounded-full bg-secondary-50 px-2 py-0.5 text-[11px] font-semibold text-secondary-700"
|
||||
title="Peserta dengan minimal 1 minat sama dengan kamu"
|
||||
>
|
||||
✨ {overlapCount} peserta sama minat
|
||||
<Sparkles size={11} strokeWidth={2} aria-hidden />
|
||||
{overlapCount} peserta sama minat
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Search } from "lucide-react";
|
||||
import { DateRangeField } from "@/components/shared/date-picker";
|
||||
import {
|
||||
formatLocalCalendarYmd,
|
||||
@@ -263,18 +264,7 @@ export function TripFilter() {
|
||||
</label>
|
||||
<div className="relative">
|
||||
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-neutral-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
className="h-4 w-4"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<Search size={16} strokeWidth={1.75} aria-hidden />
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
Reference in New Issue
Block a user