winofy-front/src/types/api.ts
2026-08-19 13:01:19 +03:30

312 lines
7.5 KiB
TypeScript

/**
* Shared API types, mirrored from winofyapifrontendintegrationguide.md + the
* OpenAPI spec (source of truth for field names/shapes). Money fields are
* integer Toman. Grouped to match src/lib/api/'s feature layout.
*
* Media fields (`icon`, `image`, `logo`, `cover_image`) are MinIO object keys,
* not URLs — every one has a `<field>_url` sibling that's a freshly presigned,
* already-absolute URL. Always render the `_url` field directly; never resolve
* the raw key yourself, and don't cache `_url` past the response — it's
* re-signed (and can change) on every read.
*/
// ---- Locations --------------------------------------------------------------
export interface City {
uuid: string;
name: string;
slug?: string;
}
export interface Neighborhood {
uuid: string;
name: string;
slug?: string;
/** Nested object, confirmed against staging — not a bare uuid string. */
city: City;
}
// ---- Stores / Catalog ---------------------------------------------------
export interface StoreCategory {
uuid: string;
name: string;
icon: string | null;
icon_url: string | null;
order: number;
}
export interface StoreListItem {
uuid: string;
name: string;
category: StoreCategory;
logo: string | null;
logo_url: string | null;
cover_image: string | null;
cover_image_url: string | null;
/** DRF serializes DecimalField as a string, e.g. "3.76" — confirmed against staging. */
rating_avg: string;
rating_count: number;
min_order_amount: number;
delivery_fee: number;
free_delivery_threshold: number | null;
is_open: boolean;
}
export interface WorkingHours {
weekday: 0 | 1 | 2 | 3 | 4 | 5 | 6; // 0 = Saturday .. 6 = Friday
opens_at: string | null;
closes_at: string | null;
is_closed: boolean;
}
export interface StoreDetail extends StoreListItem {
description: string;
address: string;
phone_number: string;
working_hours: WorkingHours[];
accepts_wallet: boolean;
accepts_online: boolean;
accepts_cash_on_delivery: boolean;
}
export type UnitType = "gram" | "ml" | "piece";
export interface ProductCategory {
uuid: string;
name: string;
icon: string | null;
icon_url: string | null;
parent: string | null;
}
export interface ProductListItem {
uuid: string;
name: string;
image: string | null;
image_url: string | null;
price: number;
unit_type: UnitType;
unit_value: number;
is_active: boolean;
is_out_of_stock: boolean;
}
export interface ProductDetail extends ProductListItem {
description: string;
category: ProductCategory;
store: StoreListItem;
}
// ---- Addresses ----------------------------------------------------------
export type AddressLabel = "home" | "work" | "other";
export interface Address {
uuid: string;
label: AddressLabel;
title: string;
full_address: string;
plaque: string;
floor: string;
unit: string;
recipient_name: string;
recipient_phone: string;
city: City;
neighborhood: Neighborhood | null;
latitude: number;
longitude: number;
is_default: boolean;
created_at: string;
}
// ---- Cart -----------------------------------------------------------------
export interface CartItem {
uuid: string;
product: ProductListItem;
quantity: number;
line_total: number;
}
export interface CartGroup {
store: StoreListItem;
items: CartItem[];
items_subtotal: number;
delivery_fee: number;
total: number;
meets_minimum_order: boolean;
}
export interface Cart {
groups: CartGroup[];
grand_total: number;
}
// ---- Checkout / Orders ---------------------------------------------------
export type DeliveryType = "express" | "scheduled";
export type PaymentMethod = "wallet" | "online" | "cash_on_delivery";
export type PaymentStatus = "pending" | "paid" | "failed";
export type OrderStatus = "placed" | "preparing" | "ready_to_ship" | "handed_to_courier" | "delivered" | "cancelled";
export interface OrderItem {
uuid: string;
product: string | null;
product_name_snapshot: string;
unit_price_snapshot: number;
quantity: number;
line_total: number;
}
export interface OrderStatusLog {
from_status: string;
to_status: string;
note: string;
created_at: string;
}
export interface Order {
uuid: string;
store: StoreListItem;
status: OrderStatus;
items: OrderItem[];
items_subtotal: number;
delivery_fee: number;
commission_amount: number;
seller_payout_amount: number;
total_amount: number;
cancel_reason: string;
courier_name: string;
courier_phone: string;
placed_at: string;
delivered_at: string | null;
status_logs: OrderStatusLog[];
is_cancellable: boolean;
}
export interface OrderGroup {
uuid: string;
recipient_name: string;
recipient_phone: string;
full_address: string;
delivery_type: DeliveryType;
scheduled_at: string | null;
payment_method: PaymentMethod;
payment_status: PaymentStatus;
notes: string;
total_amount: number;
orders: Order[];
created_at: string;
/** `feature/payment` only. */
payment?: { method: PaymentMethod; status: string; payment_url?: string };
}
// ---- Reviews --------------------------------------------------------------
export interface Review {
uuid: string;
store: string;
product: string | null;
rating: number;
comment: string;
seller_reply: string | null;
customer_name: string;
created_at: string;
}
// ---- Notifications --------------------------------------------------------
export type NotificationType = "new_order" | "settlement_done" | "new_review" | "order_cancelled";
export interface Notification {
uuid: string;
type: NotificationType;
title: string;
body: string;
related_order: string | null;
is_read: boolean;
created_at: string;
}
// ---- Seller · Store ---------------------------------------------------
export type SellerStoreStatus = "pending" | "approved" | "suspended";
export interface StoreBankAccount {
iban: string;
account_holder_name: string;
}
export interface SellerStore {
uuid: string;
name: string;
category: StoreCategory;
description: string;
logo: string | null;
logo_url: string | null;
cover_image: string | null;
cover_image_url: string | null;
phone_number: string;
city: City;
address: string;
service_neighborhoods: Neighborhood[];
/** DRF decimal-as-string, e.g. "5.00". */
delivery_radius_km: string;
min_order_amount: number;
delivery_fee: number;
free_delivery_threshold: number | null;
accepts_wallet: boolean;
accepts_online: boolean;
accepts_cash_on_delivery: boolean;
rating_avg: string;
rating_count: number;
status: SellerStoreStatus;
is_open: boolean;
bank_account: StoreBankAccount | null;
created_at: string;
}
// ---- Seller · Products ---------------------------------------------------
export interface SellerProduct {
uuid: string;
name: string;
description: string;
image: string | null;
image_url: string | null;
category: ProductCategory;
price: number;
unit_type: UnitType;
unit_value: number;
stock_quantity: number;
low_stock_threshold: number;
is_active: boolean;
sold_count: number;
is_out_of_stock: boolean;
is_low_stock: boolean;
created_at: string;
}
// ---- Seller · Analytics ---------------------------------------------------
export interface SalesChartPoint {
bucket: string;
sales: number;
}
export interface TopProduct {
name: string;
units_sold: number;
revenue: number;
}
export interface SellerAnalytics {
period: string;
order_count: number;
total_sales: number;
cancellation_rate: number;
average_order_value: number;
chart: SalesChartPoint[];
top_products: TopProduct[];
}