From ff8f6c79f6c9a0c4366d372c942ad401431e0ce6 Mon Sep 17 00:00:00 2001 From: TanKhoa1709 Date: Tue, 18 Aug 2026 22:55:02 +0700 Subject: [PATCH 1/3] fix(api): Improve listing ID handling in API generation and documentation This commit updates the API generation script to correctly handle listing ID parameters and clarifies slug resolution logic in the generated documentation. Key changes: - **API Generation**: - Updated `gen_api.sh` to map `ModelListingsIdGetIdParameter` and `ListingsIdGetIdParameter` to `String` to fix issues with `dart-dio` generator output. - Added post-processing steps to remove redundant generated model files and replace their references with `String`. - Implemented a cleanup task to remove invalid `String.fromJson` deserialization logic from `deserialize.dart`. - **Documentation**: - Updated `catalog_api.dart` comments to reflect that slugs now include IDs at the end, allowing for direct resolution without extra lookups. - Clarified that listing renames do not break existing links because the embedded ID remains the primary identifier. --- lib/api/generated/api/catalog_api.dart | 4 ++-- lib/api/generated/deserialize.dart | 2 -- lib/api/generated/model/listing.dart | 2 +- lib/api/generated/model/listing_detail.dart | 1 + .../presentation/providers/auth_provider.g.dart | 2 +- .../providers/seller_products_provider.g.dart | 2 +- tool/gen_api.sh | 15 +++++++++++++-- 7 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/api/generated/api/catalog_api.dart b/lib/api/generated/api/catalog_api.dart index 6dfcc6b..feeb097 100644 --- a/lib/api/generated/api/catalog_api.dart +++ b/lib/api/generated/api/catalog_api.dart @@ -420,7 +420,7 @@ class CatalogApi { /// Carries the variants with their prices and stock and the seller summary. A listing that was never public — `draft` or `pending` — is readable only by its owner and by staff. One that is `hidden` or soft-deleted is readable by anyone, because a cart or an order that references it still has to render; `status` and `deleted_at` say that it cannot be bought. /// /// Parameters: - /// * [id] - The listing's opaque id or its slug. A slug is lowercase, hyphen-separated and never contains an underscore, so it cannot be mistaken for an id — but only the id is accepted today; resolving a slug needs a lookup this route does not have yet. + /// * [id] - The listing's opaque id or the slug a link carries. A slug is lowercase, hyphen-separated and never contains an underscore, so it cannot be mistaken for an id; it resolves without a lookup, because it carries the id on the end. /// * [cancelToken] - A [CancelToken] that can be used to cancel the operation /// * [headers] - Can be used to add additional headers to the request /// * [extras] - Can be used to add flags to the request @@ -502,7 +502,7 @@ class CatalogApi { } /// Edit a listing - /// An edit to a draft applies immediately. An edit to a live listing that trips the prohibited-goods filter is held as a pending edit and applied only when a moderator approves it, so the version buyers see never changes into unreviewed content. The slug is fixed at creation. Renaming a listing leaves it alone: there is no redirect table, so a new slug would only turn every link already shared into a 404, and `GET /listings/{id}` takes the id as well. + /// An edit to a draft applies immediately. An edit to a live listing that trips the prohibited-goods filter is held as a pending edit and applied only when a moderator approves it, so the version buyers see never changes into unreviewed content. The slug's readable half is fixed at creation. Renaming a listing leaves it alone, so a link shared before the rename keeps saying what it said then — and it would resolve either way, since the id it carries is what addresses the listing. /// /// Parameters: /// * [id] - The opaque id. A write addresses the listing, never its public slug. diff --git a/lib/api/generated/deserialize.dart b/lib/api/generated/deserialize.dart index 6d09fd2..0d1f1a1 100644 --- a/lib/api/generated/deserialize.dart +++ b/lib/api/generated/deserialize.dart @@ -231,8 +231,6 @@ ReturnType deserialize( bool growable = true, }) { switch (targetType) { - case 'String': - return '$value' as ReturnType; case 'int': return (value is int ? value : int.parse('$value')) as ReturnType; case 'bool': diff --git a/lib/api/generated/model/listing.dart b/lib/api/generated/model/listing.dart index ba24b0d..d717edc 100644 --- a/lib/api/generated/model/listing.dart +++ b/lib/api/generated/model/listing.dart @@ -129,7 +129,7 @@ class Listing { @JsonKey(name: r'seller', required: true, includeIfNull: false) final AccountSummary seller; - /// URL-friendly, globally unique and fixed at creation. + /// The slug a link carries: the listing's name, slugified and frozen at creation, with the listing's id on the end. The text in front is for the person reading the link and is not matched — renaming a listing does not rewrite links already shared, and a stale one still resolves because the id behind the last hyphen is what addresses the listing. It never contains an underscore, which is how `GET /listings/{id}` tells it from a `ListingID` and accepts either. A name that slugifies to nothing leaves the id alone. @JsonKey(name: r'slug', required: true, includeIfNull: false) final String slug; diff --git a/lib/api/generated/model/listing_detail.dart b/lib/api/generated/model/listing_detail.dart index 0d671d3..9148e68 100644 --- a/lib/api/generated/model/listing_detail.dart +++ b/lib/api/generated/model/listing_detail.dart @@ -146,6 +146,7 @@ class ListingDetail { @JsonKey(name: r'seller', required: true, includeIfNull: false) final AccountSummary seller; + /// The slug a link carries: the listing's name, slugified and frozen at creation, with the listing's id on the end. The text in front is for the person reading the link and is not matched — renaming a listing does not rewrite links already shared, and a stale one still resolves because the id behind the last hyphen is what addresses the listing. It never contains an underscore, which is how `GET /listings/{id}` tells it from a `ListingID` and accepts either. A name that slugifies to nothing leaves the id alone. @JsonKey(name: r'slug', required: true, includeIfNull: false) final String slug; diff --git a/lib/features/auth/presentation/providers/auth_provider.g.dart b/lib/features/auth/presentation/providers/auth_provider.g.dart index 561c3a2..7ea39b6 100644 --- a/lib/features/auth/presentation/providers/auth_provider.g.dart +++ b/lib/features/auth/presentation/providers/auth_provider.g.dart @@ -41,7 +41,7 @@ final class AuthNotifierProvider } } -String _$authNotifierHash() => r'ca2a9efa27409be3254e2a512e2b9542d4151a23'; +String _$authNotifierHash() => r'b2e5ce59d4d17725098f65f1f6b01b9eebf18b60'; abstract class _$AuthNotifier extends $Notifier { AuthState build(); diff --git a/lib/features/seller/presentation/providers/seller_products_provider.g.dart b/lib/features/seller/presentation/providers/seller_products_provider.g.dart index cef795a..9655748 100644 --- a/lib/features/seller/presentation/providers/seller_products_provider.g.dart +++ b/lib/features/seller/presentation/providers/seller_products_provider.g.dart @@ -175,7 +175,7 @@ final class SellerProductsNotifierProvider } String _$sellerProductsNotifierHash() => - r'd2f7780c2e08be98b38bfa2ea8c873c4b0524cae'; + r'b4b7796cf3a109c52c06a2f503ba35814797536f'; abstract class _$SellerProductsNotifier extends $Notifier { SellerProductsState build(); diff --git a/tool/gen_api.sh b/tool/gen_api.sh index 0898b29..38b4a95 100755 --- a/tool/gen_api.sh +++ b/tool/gen_api.sh @@ -31,8 +31,8 @@ npx --yes @openapitools/openapi-generator-cli generate \ -g dart-dio \ -o "$TMP/pkg" \ --additional-properties=serializationLibrary=json_serializable,pubName=shopnexus_api \ - --schema-mappings SemanticSeed=String \ - --type-mappings SemanticSeed=String,date=String + --schema-mappings SemanticSeed=String,ModelListingsIdGetIdParameter=String,ListingsIdGetIdParameter=String \ + --type-mappings SemanticSeed=String,ModelListingsIdGetIdParameter=String,ListingsIdGetIdParameter=String,date=String # The generator emits a standalone package. The app hosts the code directly # instead, so there is one pubspec, one build_runner and one dio — a nested @@ -42,6 +42,17 @@ mkdir -p "$OUT" mv "$TMP/pkg/lib/src"/* "$OUT/" mv "$TMP/pkg/lib/shopnexus_api.dart" "$OUT/shopnexus_api.dart" +# Fix broken dart-dio generator output for empty oneOf parameter (ModelListingsIdGetIdParameter) +rm -f "$OUT/model/model_listings_id_get_id_parameter.dart" +grep -rl 'ModelListingsIdGetIdParameter' "$OUT" \ + | xargs sed -i -e "s#ModelListingsIdGetIdParameter#String#g" + +grep -rl 'model_listings_id_get_id_parameter' "$OUT" \ + | xargs sed -i -e "/model_listings_id_get_id_parameter/d" + +# Clean up invalid String.fromJson deserialize entry +sed -i "/case 'String':/,/as ReturnType;/d" "$OUT/deserialize.dart" + # Rewrite the standalone package's self-imports onto this package. grep -rl 'package:shopnexus_api/' "$OUT" \ | xargs sed -i \ From 23681e1f090f4b16b0724646e2599c53249d2645 Mon Sep 17 00:00:00 2001 From: TanKhoa1709 Date: Tue, 18 Aug 2026 23:07:56 +0700 Subject: [PATCH 2/3] feat(checkout): Add buyer note functionality to checkout process This commit introduces a "Note for seller" feature during the checkout flow, allowing users to provide specific instructions or requests alongside their order. Key changes: - **State Management**: Added a `note` field to `CheckoutState` and implemented a `setNote` method in `CheckoutNotifier` to manage the input state. - **Data Layer**: Updated the checkout submission logic in `checkout_provider.dart` to include the trimmed note when creating the final checkout object. - **UI Components**: - **Buyer Note Card**: Created a new `_buildBuyerNoteCard` widget featuring a stylized multi-line `TextField` with a 200-character limit and dark mode support. - **Input Handling**: Integrated `TextEditingController` within `CheckoutScreen` to synchronize UI input with the state provider and ensure proper cleanup during the widget lifecycle. - **Visual Improvements**: Standardized the styling of the new note section to match existing checkout cards, including consistent shadows, borders, and typography. --- .../providers/checkout_provider.dart | 9 ++ .../providers/checkout_provider.freezed.dart | 39 ++--- .../providers/checkout_provider.g.dart | 2 +- .../presentation/screens/checkout_screen.dart | 133 +++++++++++++++++- 4 files changed, 163 insertions(+), 20 deletions(-) diff --git a/lib/features/checkout/presentation/providers/checkout_provider.dart b/lib/features/checkout/presentation/providers/checkout_provider.dart index b112f94..5d60f3f 100644 --- a/lib/features/checkout/presentation/providers/checkout_provider.dart +++ b/lib/features/checkout/presentation/providers/checkout_provider.dart @@ -65,6 +65,7 @@ abstract class CheckoutState with _$CheckoutState { /// checkout that breaks the day an operator retires that rail. @Default([]) List