Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/screenshots/ux-2026-07-10/01-login.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/ux-2026-07-10/02-dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/ux-2026-07-10/03-fleet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/ux-2026-07-10/04-orders.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/ux-2026-07-10/06-drivers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/ux-2026-07-10/07-reports.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions docs/screenshots/ux-2026-07-10/report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"date": "2026-07-10",
"base": "http://localhost:5173",
"rideId": "4",
"passed": 6,
"failed": 1,
"results": [
{
"page": "01-login",
"url": "http://localhost:5173/login",
"screenshot": "/Users/mac/Documents/line-fleet-admin/docs/screenshots/ux-2026-07-10/01-login.png",
"status": "pass",
"notes": ""
},
{
"page": "02-dashboard",
"url": "http://localhost:5173/",
"screenshot": "/Users/mac/Documents/line-fleet-admin/docs/screenshots/ux-2026-07-10/02-dashboard.png",
"status": "pass",
"notes": ""
},
{
"page": "03-fleet",
"url": "http://localhost:5173/fleet",
"screenshot": "/Users/mac/Documents/line-fleet-admin/docs/screenshots/ux-2026-07-10/03-fleet.png",
"status": "pass",
"notes": "",
"mapCanvas": true
},
{
"page": "04-orders",
"url": "http://localhost:5173/orders",
"screenshot": "/Users/mac/Documents/line-fleet-admin/docs/screenshots/ux-2026-07-10/04-orders.png",
"status": "pass",
"notes": "",
"tableRows": 0
},
{
"page": "05-order-detail",
"url": "http://localhost:5173/orders/4",
"screenshot": "/Users/mac/Documents/line-fleet-admin/docs/screenshots/ux-2026-07-10/05-order-detail.png",
"status": "fail",
"notes": "page.waitForSelector: Timeout 20000ms exceeded.\nCall log:\n - waiting for locator('text=訂單 #') to be visible\n"
},
{
"page": "06-drivers",
"url": "http://localhost:5173/drivers",
"screenshot": "/Users/mac/Documents/line-fleet-admin/docs/screenshots/ux-2026-07-10/06-drivers.png",
"status": "pass",
"notes": "",
"tableRows": 0
},
{
"page": "07-reports",
"url": "http://localhost:5173/reports",
"screenshot": "/Users/mac/Documents/line-fleet-admin/docs/screenshots/ux-2026-07-10/07-reports.png",
"status": "pass",
"notes": "",
"tableRows": 0
}
]
}
54 changes: 54 additions & 0 deletions src/api/admin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
normalizeDriver,
parseTrackCoordinates,
patchDriverStatus,
reviewDriverVehicle,
updateAdmin,
updateDispatchSettings,
updateFeeSettings,
Expand Down Expand Up @@ -77,6 +78,10 @@ describe('normalizeDriver', () => {
Name: '司機A',
Phone: '0912',
Status: 1,
VehicleType: '',
PlateNumber: '',
VehicleReviewStatus: '',
VehicleReviewNote: '',
CreatedAt: '2026-07-11T13:09:08+08:00',
UpdatedAt: '2026-07-11T16:46:44+08:00',
});
Expand All @@ -97,11 +102,33 @@ describe('normalizeDriver', () => {
Name: '司機B',
Phone: '',
Status: 3,
VehicleType: '',
PlateNumber: '',
VehicleReviewStatus: '',
VehicleReviewNote: '',
CreatedAt: '2026-07-10T00:00:00Z',
UpdatedAt: '2026-07-10T01:00:00Z',
});
});

it('解析車輛與審核欄位(O5)', () => {
const d = normalizeDriver({
ID: 5,
LineUserID: 'l',
Name: 'D',
Phone: '',
Status: 1,
vehicle_type: 'pet',
plate_number: 'PET-0001',
vehicle_review_status: 'rejected',
vehicle_review_note: '車牌照片模糊',
});
expect(d.VehicleType).toBe('pet');
expect(d.PlateNumber).toBe('PET-0001');
expect(d.VehicleReviewStatus).toBe('rejected');
expect(d.VehicleReviewNote).toBe('車牌照片模糊');
});

it('缺時間欄位時回空字串', () => {
const d = normalizeDriver({ ID: 3, LineUserID: 'l', Name: 'C', Phone: '', Status: 0 });
expect(d.CreatedAt).toBe('');
Expand Down Expand Up @@ -256,6 +283,33 @@ describe('P2 admin write APIs', () => {
expect(driver.Status).toBe(3);
});

it('reviewDriverVehicle 核准', async () => {
vi.mocked(api.post).mockResolvedValue({
data: { driver: { ID: 4, Name: 'A', LineUserID: 'U4', Status: 0, vehicle_review_status: 'approved' } },
});
const driver = await reviewDriverVehicle(4, true);
expect(api.post).toHaveBeenCalledWith('/admin/drivers/4/vehicle-review', { approve: true, note: '' });
expect(driver.VehicleReviewStatus).toBe('approved');
});

it('reviewDriverVehicle 退回帶原因', async () => {
vi.mocked(api.post).mockResolvedValue({
data: {
driver: {
ID: 4, Name: 'A', LineUserID: 'U4', Status: 0,
vehicle_review_status: 'rejected', vehicle_review_note: '車牌模糊',
},
},
});
const driver = await reviewDriverVehicle(4, false, '車牌模糊');
expect(api.post).toHaveBeenCalledWith('/admin/drivers/4/vehicle-review', {
approve: false,
note: '車牌模糊',
});
expect(driver.VehicleReviewStatus).toBe('rejected');
expect(driver.VehicleReviewNote).toBe('車牌模糊');
});

it('fetchDispatchSettings', async () => {
vi.mocked(api.get).mockResolvedValue({
data: { radius_m: 3000, max_drivers: 5, offer_timeout_sec: 20, max_attempts: 3, rate_limit_per_min: 5 },
Expand Down
21 changes: 21 additions & 0 deletions src/api/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export interface Driver {
Name: string;
Phone: string;
Status: number; // 0=離線 1=待命 2=載客中
VehicleType: string; // 車種 code(O1);'' 為未填
PlateNumber: string; // 車牌(O1);'' 為未填
VehicleReviewStatus: string; // 審核狀態(O5):''/pending/approved/rejected
VehicleReviewNote: string; // 退回原因(O5)
CreatedAt: string; // 後端回傳的 ISO 時間字串;缺值為空字串
UpdatedAt: string;
}
Expand Down Expand Up @@ -164,6 +168,10 @@ export function normalizeDriver(raw: Record<string, unknown>): Driver {
Name: str(raw, 'Name', 'name'),
Phone: str(raw, 'Phone', 'phone'),
Status: num(raw, 'Status', 'status'),
VehicleType: str(raw, 'VehicleType', 'vehicle_type'),
PlateNumber: str(raw, 'PlateNumber', 'plate_number'),
VehicleReviewStatus: str(raw, 'VehicleReviewStatus', 'vehicle_review_status'),
VehicleReviewNote: str(raw, 'VehicleReviewNote', 'vehicle_review_note'),
CreatedAt: str(raw, 'CreatedAt', 'created_at'),
UpdatedAt: str(raw, 'UpdatedAt', 'updated_at'),
};
Expand Down Expand Up @@ -411,6 +419,19 @@ export async function patchDriverStatus(id: number, enabled: boolean): Promise<D
return data.driver;
}

// 車輛審核(O5):approve=false 時 note 必填(退回原因)。回傳正規化前的 raw driver。
export async function reviewDriverVehicle(
id: number,
approve: boolean,
note = '',
): Promise<Driver> {
const { data } = await api.post<{ driver: Record<string, unknown> }>(
`/admin/drivers/${id}/vehicle-review`,
{ approve, note },
);
return normalizeDriver(data.driver);
}

export async function fetchDispatchSettings(): Promise<DispatchSettings> {
const { data } = await api.get<DispatchSettings>('/admin/settings/dispatch');
return data;
Expand Down
17 changes: 17 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ export const DRIVER_STATUS: Record<number, { label: string; color: string }> = {
/** 後端 DriverStatusDisabled */
export const DRIVER_STATUS_DISABLED = 3;

/** 車種顯示名(對齊後端 constants/vehicle.go 的 code) */
export const VEHICLE_TYPE_LABEL: Record<string, string> = {
sedan: '轎車',
suv: '休旅車',
van7: '七人座',
accessible: '無障礙車',
pet: '寵物用車',
};

/** 車輛審核狀態(O5,對齊後端 constants.VehicleReview*) */
export const VEHICLE_REVIEW_STATUS: Record<string, { label: string; color: string }> = {
'': { label: '未填車輛', color: 'default' },
pending: { label: '待審核', color: 'warning' },
approved: { label: '已核准', color: 'success' },
rejected: { label: '已退回', color: 'error' },
};

/** 後台可強制取消的訂單狀態(已上車/已完成/已取消不可) */
export function isRideCancellable(status: number): boolean {
return status >= 0 && status <= 2;
Expand Down
Loading
Loading