webcake-storefront-mcp 1.15.1 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.js CHANGED
@@ -520,4 +520,469 @@ export class WebcakeCmsApi {
520
520
  sendMail(params) {
521
521
  return this.request("POST", `/api/v1/cms_function/${this.siteId}/application/automation/send_mail`, { body: params });
522
522
  }
523
+ // ── App lifecycle (applications/subcriptions) ──
524
+ /** Uninstall an app by its subscription id (the `id` from list_apps / get_app). */
525
+ uninstallApp(id) {
526
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/subcriptions/uninstall_app`, { body: { id } });
527
+ }
528
+ /** Update an installed app's settings. `attrs` is merged onto the subscription (e.g. { settings: {...}, status }). */
529
+ updateApp(id, attrs) {
530
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/subcriptions/update_app`, { body: { id, attrs } });
531
+ }
532
+ /** Update the product-review app's settings (shop_info, display options…). */
533
+ updateAppReview(id, settings) {
534
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/subcriptions/update_app_review`, { body: { id, settings } });
535
+ }
536
+ // ── Product reviews (product_review app) ──
537
+ listReviews(query) {
538
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/applications/product_reviews/all_review_admin`, { query });
539
+ }
540
+ getReview(reviewId) {
541
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/applications/product_reviews/${reviewId}`);
542
+ }
543
+ getReviewProducts(reviewId) {
544
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/applications/product_reviews/products`, { query: { review_id: reviewId } });
545
+ }
546
+ /** Create or update a review/reply. `body` is the review payload (id, customer_info, rating, title,
547
+ * comment, images, parent_id, apply_to, categories, permission, is_shop, product_reviews…). */
548
+ createOrUpdateReview(body) {
549
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/product_reviews/create_or_update`, { body });
550
+ }
551
+ removeReviews(ids) {
552
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/product_reviews/remove`, { body: { ids } });
553
+ }
554
+ /** Approve/hide reviews. `payload` typically { ids, permission } (permission 0=approved, 1=pending…). */
555
+ updateReviewStatus(payload) {
556
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/product_reviews/update_status`, { body: payload });
557
+ }
558
+ // ── Appointment app ──
559
+ listAppointmentCalendars(query) {
560
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/appointment/appointment_calendars`, { query });
561
+ }
562
+ createAppointmentCalendar(appointment_calendar) {
563
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/create_appointment_calendar`, { body: { appointment_calendar } });
564
+ }
565
+ updateAppointmentCalendar(appointment_calendar) {
566
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/update_appointment_calendar`, { body: { appointment_calendar } });
567
+ }
568
+ deleteAppointmentCalendars(ids) {
569
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/delete_appointment_calendars`, { body: { ids } });
570
+ }
571
+ duplicateAppointmentCalendars(ids) {
572
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/duplicate_appointment_calendars`, { body: { ids } });
573
+ }
574
+ listAppointments(query) {
575
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/appointment/appointments`, { query });
576
+ }
577
+ listAppointmentAddresses(query) {
578
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/appointment/appointment_addresses`, { query });
579
+ }
580
+ createAppointmentAddress(appointment_address) {
581
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/create_appointment_address`, { body: { appointment_address } });
582
+ }
583
+ updateAppointmentAddress(appointment_address) {
584
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/update_appointment_address`, { body: { appointment_address } });
585
+ }
586
+ deleteAppointmentAddresses(ids) {
587
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/delete_appointment_addresses`, { body: { ids } });
588
+ }
589
+ listAppointmentClassifies(query) {
590
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/appointment/appointment_classifies`, { query });
591
+ }
592
+ createAppointmentClassify(appointment_classify) {
593
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/create_appointment_classify`, { body: { appointment_classify } });
594
+ }
595
+ updateAppointmentClassify(appointment_classify) {
596
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/update_appointment_classify`, { body: { appointment_classify } });
597
+ }
598
+ deleteAppointmentClassifies(ids) {
599
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/delete_appointment_classifies`, { body: { ids } });
600
+ }
601
+ listAppointmentEmployees(query) {
602
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/appointment/appointment_employees`, { query });
603
+ }
604
+ createAppointmentEmployee(appointment_employee) {
605
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/create_appointment_employee`, { body: { appointment_employee } });
606
+ }
607
+ updateAppointmentEmployee(appointment_employee) {
608
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/update_appointment_employee`, { body: { appointment_employee } });
609
+ }
610
+ deleteAppointmentEmployees(ids) {
611
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/appointment/delete_appointment_employees`, { body: { ids } });
612
+ }
613
+ // ── Affiliate app ──
614
+ getProgramAffiliates() {
615
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/affiliate/program_affiliates`);
616
+ }
617
+ getProductAffiliates(query) {
618
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/affiliate/product_affiliates`, { query });
619
+ }
620
+ getOrderAffiliates(query) {
621
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/affiliate/order_affiliates`, { query });
622
+ }
623
+ getAccountAffiliates(query) {
624
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/affiliate/account_affiliates`, { query });
625
+ }
626
+ getPayoutAffiliates(query) {
627
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/affiliate/payout_affiliates`, { query });
628
+ }
629
+ getAffiliateStatistic() {
630
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/affiliate/statistic`);
631
+ }
632
+ upsertOrderProgramAffiliate(payload) {
633
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/affiliate/upsert_order_program_affiliate`, { body: payload });
634
+ }
635
+ upsertProductProgramAffiliate(payload) {
636
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/affiliate/upsert_product_program_affiliate`, { body: payload });
637
+ }
638
+ upsertProductAffiliate(payload) {
639
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/affiliate/upsert_product_affiliate`, { body: payload });
640
+ }
641
+ deleteProductAffiliates(payload) {
642
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/affiliate/delete_product_affiliates`, { body: payload });
643
+ }
644
+ updatePayoutAffiliateStatus(payload) {
645
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/affiliate/update_status_payout_affiliate`, { body: payload });
646
+ }
647
+ deleteAccountAffiliates(payload) {
648
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/affiliate/delete_account_affiliates`, { body: payload });
649
+ }
650
+ updateAccountAffiliate(payload) {
651
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/affiliate/update_account_affiliate`, { body: payload });
652
+ }
653
+ // ── Catalog extras (brands / suppliers / tags / ribbons / materials / variations) ──
654
+ listProductBrands() {
655
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/products/product_brands/all`);
656
+ }
657
+ upsertProductBrand(body) {
658
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/products/product_brands/create_or_update`, { body });
659
+ }
660
+ listProductSuppliers() {
661
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/products/product_suppliers/all`);
662
+ }
663
+ upsertProductSupplier(body) {
664
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/products/product_suppliers/create_or_update`, { body });
665
+ }
666
+ listProductTags(query) {
667
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/products/product_tags/all`, { query });
668
+ }
669
+ upsertProductTag(body) {
670
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/products/product_tags/create_or_update`, { body });
671
+ }
672
+ listRibbons() {
673
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/products/ribbons/all`);
674
+ }
675
+ upsertRibbon(body) {
676
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/products/ribbons/create_or_update`, { body });
677
+ }
678
+ listMaterials() {
679
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/products/materials/all`);
680
+ }
681
+ upsertMaterial(body) {
682
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/products/materials/create_or_update`, { body });
683
+ }
684
+ getVariation(variationId) {
685
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/products/variation/${variationId}`);
686
+ }
687
+ getProductMeasurement() {
688
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/settings/product_measurement`);
689
+ }
690
+ updateProductMeasurement(product_measurement) {
691
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/settings/product_measurement/update`, { body: { product_measurement } });
692
+ }
693
+ listBlockPhoneNumbers(query) {
694
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/settings/block_phone_number/all`, { query });
695
+ }
696
+ upsertBlockPhoneCustomers(phone_numbers) {
697
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/settings/block_phone_number/upsert_block_phone_customers`, { body: { phone_numbers } });
698
+ }
699
+ removeAllBlockPhoneCustomers() {
700
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/settings/block_phone_number/remove_all_block_phone_customers`, { body: {} });
701
+ }
702
+ getPriceContacts(type) {
703
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/settings/price_contact/get`, { query: { type } });
704
+ }
705
+ updatePriceContact(body) {
706
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/settings/price_contact/update`, { body });
707
+ }
708
+ getCategoryById(categoryId) {
709
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/categories/${categoryId}`);
710
+ }
711
+ // ── Site config (domains / redirects / shipping / utms / logs / filters / settings / fonts) ──
712
+ listDomains(query) {
713
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/domains/all`, { query });
714
+ }
715
+ createDomain(body) {
716
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/domains/create`, { body });
717
+ }
718
+ updateDomain(body) {
719
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/domains/update`, { body });
720
+ }
721
+ verifyDomain(domain_id) {
722
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/domains/verify`, { body: { domain_id } });
723
+ }
724
+ deleteDomain(domain_id) {
725
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/domains/delete`, { body: { domain_id } });
726
+ }
727
+ checkDomain(domain) {
728
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/domains/check_domain`, { body: { domain } });
729
+ }
730
+ listRedirectUrls(query) {
731
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/seos/redirect_urls/all`, { query });
732
+ }
733
+ createRedirectUrl(redirect_url) {
734
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/seos/redirect_urls/create`, { body: { redirect_url } });
735
+ }
736
+ updateRedirectUrl(redirect_url) {
737
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/seos/redirect_urls/update`, { body: { redirect_url } });
738
+ }
739
+ deleteRedirectUrls(redirect_url_ids) {
740
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/seos/redirect_urls/delete`, { body: { redirect_url_ids } });
741
+ }
742
+ getShipping() {
743
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/settings/shipping`);
744
+ }
745
+ updateShipping(body) {
746
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/settings/shipping/update`, { body });
747
+ }
748
+ listSiteUtms(query) {
749
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/site_utms/all`, { query });
750
+ }
751
+ createSiteUtm(site_utm) {
752
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/site_utms/create`, { body: { site_utm } });
753
+ }
754
+ updateSiteUtm(site_utm) {
755
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/site_utms/update`, { body: { site_utm } });
756
+ }
757
+ deleteSiteUtms(ids) {
758
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/site_utms/delete`, { body: { ids } });
759
+ }
760
+ listSystemLogs(query) {
761
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/system_logs/all`, { query });
762
+ }
763
+ listSavedFilters(table_key) {
764
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/saved_filters/`, { query: { table_key } });
765
+ }
766
+ createSavedFilter(saved_filter) {
767
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/saved_filters/create`, { body: { saved_filter } });
768
+ }
769
+ updateSavedFilter(id, saved_filter) {
770
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/saved_filters/${id}/update`, { body: { saved_filter } });
771
+ }
772
+ deleteSavedFilter(id) {
773
+ return this.request("DELETE", `/api/v1/dashboard/site/${this.siteId}/saved_filters/${id}`);
774
+ }
775
+ /** Raw /update_site call. `body` top-level keys replace; a nested `settings` key is merged server-side. */
776
+ updateSiteRaw(body) {
777
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/update_site`, { body });
778
+ }
779
+ updateSiteName(name) {
780
+ return this.request("POST", `/api/v1/site/${this.siteId}/update_site_name`, { body: { name } });
781
+ }
782
+ updateSiteSlug(slug) {
783
+ return this.request("POST", `/api/v1/site/${this.siteId}/update_slug_name`, { body: { slug } });
784
+ }
785
+ loadFonts() {
786
+ return this.request("GET", `/api/v1/site/${this.siteId}/load_fonts`);
787
+ }
788
+ removeFont(id) {
789
+ return this.request("DELETE", `/api/v1/site/${this.siteId}/remove_font`, { body: { id } });
790
+ }
791
+ loadFontGroups() {
792
+ return this.request("GET", `/api/v1/site/${this.siteId}/load_font_groups`);
793
+ }
794
+ createFontGroup(body) {
795
+ return this.request("POST", `/api/v1/site/${this.siteId}/create_font_group`, { body: { ...body, site_id: this.siteId } });
796
+ }
797
+ removeFontGroup(id) {
798
+ return this.request("DELETE", `/api/v1/site/${this.siteId}/remove_font_groups`, { body: { id, site_id: this.siteId } });
799
+ }
800
+ listApiKeys(query) {
801
+ return this.request("GET", `/api/v1/site/${this.siteId}/api_keys/all`, { query });
802
+ }
803
+ listPublishHistories(query) {
804
+ return this.request("GET", `/api/v1/site/${this.siteId}/publish_histories`, { query });
805
+ }
806
+ // ── Marketing / CRM / team ──
807
+ getSendEmail() {
808
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/send_email/all`);
809
+ }
810
+ saveSendEmail(body) {
811
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/send_email/save`, { body });
812
+ }
813
+ listContacts(query) {
814
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/contact/all`, { query });
815
+ }
816
+ createContact(body) {
817
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/contact/create`, { body: { ...body, site_id: this.siteId } });
818
+ }
819
+ deleteContacts(ids) {
820
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/contact/delete`, { body: { ids } });
821
+ }
822
+ listSubscribers(query) {
823
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/subscriber/all`, { query });
824
+ }
825
+ createSubscriber(body) {
826
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/subscriber/create`, { body });
827
+ }
828
+ deleteSubscribers(ids) {
829
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/subscriber/delete`, { body: { ids } });
830
+ }
831
+ listEmployees() {
832
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/settings/employee/all`);
833
+ }
834
+ inviteEmployee(email) {
835
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/settings/employee/invite`, { body: { email } });
836
+ }
837
+ updateEmployeePermissions(site_permission) {
838
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/settings/employee/change_permissions`, { body: { site_permission } });
839
+ }
840
+ deleteEmployees(site_permission_ids) {
841
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/settings/employee/delete_employees`, { body: { site_permission_ids } });
842
+ }
843
+ listInvitations() {
844
+ return this.request("GET", `/api/v1/dashboard/site/invitations/all`);
845
+ }
846
+ acceptInvitation(invitation_id) {
847
+ return this.request("POST", `/api/v1/dashboard/site/invitations/accept`, { body: { invitation_id } });
848
+ }
849
+ refuseInvitation(invitation_id) {
850
+ return this.request("POST", `/api/v1/dashboard/site/invitations/refuse`, { body: { invitation_id } });
851
+ }
852
+ listCustomerTags() {
853
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/customer/customer_tags`);
854
+ }
855
+ upsertCustomerTag(body) {
856
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/customer/upsert_customer_tag`, { body: { ...body, site_id: this.siteId } });
857
+ }
858
+ assignCustomerTags(body) {
859
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/customer/update_tags_for_multiple_customers`, { body: { ...body, site_id: this.siteId } });
860
+ }
861
+ getInsightToday() {
862
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/insight/today`);
863
+ }
864
+ getInsight(query) {
865
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/insight/all`, { query });
866
+ }
867
+ listNotifications() {
868
+ return this.request("GET", `/api/v1/dashboard/site/notifications/all`);
869
+ }
870
+ markNotificationRead(notification_id) {
871
+ return this.request("POST", `/api/v1/dashboard/site/notifications/mark_as_read`, { body: { notification_id } });
872
+ }
873
+ // ── Multilingual / translations ──
874
+ mlAddLanguages(languages) {
875
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/multilingual/add_multiple_language`, { body: { languages } });
876
+ }
877
+ mlChangeDefault(language_code) {
878
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/multilingual/change_language_default`, { body: { language_code } });
879
+ }
880
+ mlGetTranslations(resource, query) {
881
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/multilingual/${resource}`, { query });
882
+ }
883
+ mlSaveTranslations(action, body) {
884
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/multilingual/${action}`, { body });
885
+ }
886
+ mlTranslateJson(body) {
887
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/multilingual/translate_json`, { body });
888
+ }
889
+ // ── Media library + PWA ──
890
+ listMediaFolders(query) {
891
+ return this.request("GET", `/api/v1/site/${this.siteId}/media/folder`, { query });
892
+ }
893
+ listMediaContent(query) {
894
+ return this.request("GET", `/api/v1/site/${this.siteId}/media/content`, { query });
895
+ }
896
+ listMediaAll(query) {
897
+ return this.request("GET", `/api/v1/site/${this.siteId}/media/all`, { query });
898
+ }
899
+ getMediaCapacity() {
900
+ return this.request("GET", `/api/v1/site/${this.siteId}/media/capacity`);
901
+ }
902
+ emptyMediaTrash() {
903
+ return this.request("POST", `/api/v1/site/${this.siteId}/media/empty_trash`, { body: {} });
904
+ }
905
+ uploadBase64Media(base64) {
906
+ return this.request("POST", `/api/v1/site/${this.siteId}/media/content/b64`, { body: { base64 }, timeout: 60000 });
907
+ }
908
+ updateMediaFolder(body) {
909
+ return this.request("POST", `/api/v1/site/${this.siteId}/media/folder/update`, { body });
910
+ }
911
+ updateMediaContent(body) {
912
+ return this.request("POST", `/api/v1/site/${this.siteId}/media/content/update`, { body });
913
+ }
914
+ getPwa() {
915
+ return this.request("GET", `/api/v1/site/${this.siteId}/pwa`);
916
+ }
917
+ upsertPwa(body) {
918
+ return this.request("POST", `/api/v1/site/${this.siteId}/pwa/upsert`, { body });
919
+ }
920
+ // ── Apps: product design / personal product design / course ──
921
+ getDeviceTemplates() {
922
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/applications/product_design/get_group_device_tempalte`);
923
+ }
924
+ createProductsByDeviceTemplate(groups) {
925
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/product_design/create_product_by_device_template`, { body: { groups, site_id: this.siteId } });
926
+ }
927
+ getAllPpd(query) {
928
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/applications/personal_product_design/get_all_ppd`, { query });
929
+ }
930
+ upsertPpd(ppds) {
931
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/personal_product_design/upsert_ppd`, { body: { ppds, site_id: this.siteId } });
932
+ }
933
+ removePpd(ids) {
934
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/personal_product_design/remove_ppd`, { body: { ids, site_id: this.siteId } });
935
+ }
936
+ getProductVariationTemplate(product_id) {
937
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/applications/personal_product_design/get_product_variation_template`, { query: { product_id } });
938
+ }
939
+ listCourses(query) {
940
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/course-app`, { query });
941
+ }
942
+ getCourse(id) {
943
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/course-app/${id}`);
944
+ }
945
+ createCourse(body) {
946
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/course-app`, { body: { ...body, site_id: this.siteId } });
947
+ }
948
+ updateCourse(id, body) {
949
+ return this.request("PATCH", `/api/v1/dashboard/site/${this.siteId}/course-app/${id}`, { body });
950
+ }
951
+ deleteCourse(id) {
952
+ return this.request("DELETE", `/api/v1/dashboard/site/${this.siteId}/course-app/${id}`);
953
+ }
954
+ deleteManyCourses(ids) {
955
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/course-app/delete_many`, { body: { ids, site_id: this.siteId } });
956
+ }
957
+ getCourseMembers(query) {
958
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/course-app/members`, { query });
959
+ }
960
+ // ── Sale channels: sitemap + partner feeds + catalogs ──
961
+ sitemapSync(kind) {
962
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/sale_channels/sitemap/sync_${kind}`, { body: {} });
963
+ }
964
+ sitemapRebuild(type) {
965
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/sale_channels/sitemap/rebuild`, { body: { type } });
966
+ }
967
+ listPartnerFeeds(query) {
968
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/sale_channels/partner_feeds/all`, { query });
969
+ }
970
+ createPartnerFeed(body) {
971
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/sale_channels/partner_feeds/create`, { body });
972
+ }
973
+ updatePartnerFeed(partnerFeedId, body) {
974
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/sale_channels/partner_feeds/${partnerFeedId}/update`, { body });
975
+ }
976
+ deletePartnerFeeds(partner_feed_ids) {
977
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/sale_channels/partner_feeds/delete`, { body: { partner_feed_ids } });
978
+ }
979
+ listPartnerFeedProducts(partnerFeedId, query) {
980
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/sale_channels/partner_feeds/${partnerFeedId}/products/all`, { query });
981
+ }
982
+ syncPartnerFeed(partnerFeedId) {
983
+ return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/sale_channels/partner_feeds/${partnerFeedId}/products/sync`, { body: { partner_feed_id: partnerFeedId } });
984
+ }
985
+ listGoogleMerchants(query) {
986
+ return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/sale_channels/google/merchants`, { query });
987
+ }
523
988
  }
@@ -36,7 +36,26 @@ children with a grid:
36
36
  - each child config also has \`rowStart/rowEnd\` (1-based grid lines),
37
37
  \`constraintX\` (['left'|'right'|'centerLeft']), \`constraintY\` (['top'|'bottom'|'centerTop']).
38
38
  new_section does ALL of this for you: pass children and they are stacked one row each in
39
- the centre column. To build multi-column layouts, nest a container child with its own grid.
39
+ the centre column.
40
+
41
+ ## Multi-column rows (cards side by side) — USE THIS, real pages are full of them
42
+ A plain vertical stack looks like a blog post, not a designed page. Feature cards,
43
+ category tiles, footer columns, a text+image hero — all are HORIZONTAL rows. Two ways:
44
+ - new_row(children=[colA, colB, colC]) → a container whose children sit SIDE BY SIDE in
45
+ equal columns. Place the returned node as a child of a section.
46
+ - Inside new_section, add \`layout:"row"\` to a CONTAINER child spec and its children become
47
+ columns: \`{ type:"container", layout:"row", children:[ {..}, {..}, {..} ] }\`.
48
+ Rows are RESPONSIVE automatically: finalizeForRender collapses them on small screens
49
+ (default tablet=2 columns, mobile=1) so cards never shrink to slivers — override with
50
+ \`collapse:{bp3:2,bp4:1}\`. Uneven columns via \`colWidths\` (e.g. a 2:1 text/image split:
51
+ \`colWidths:[{unit:'fr',value:2},{unit:'fr',value:1}]\`), spacing via \`columnGap\`/\`rowGap\`.
52
+ Each column is usually a \`container\` holding its own stacked children (image + title +
53
+ text). Example feature row:
54
+ \`new_row(children=[
55
+ { type:"container", children:[ {type:"image",opts:{...}}, {type:"text",opts:{text:"Giao nhanh"}} ] },
56
+ { type:"container", children:[ {type:"image",opts:{...}}, {type:"text",opts:{text:"Chất lượng"}} ] },
57
+ { type:"container", children:[ {type:"image",opts:{...}}, {type:"text",opts:{text:"Bảo hành"}} ] },
58
+ ])\`
40
59
 
41
60
  ## Where layout/style live: per-breakpoint keys (NOT \`runtime\`)
42
61
  new_section / new_element emit a temporary \`runtime: { style, config }\`. That is a
@@ -84,6 +84,68 @@ export function stackChildren(container, children, opts = {}) {
84
84
  container.children = children;
85
85
  return container;
86
86
  }
87
+ const ROW_PLACEHOLDER_ROW = () => ({ unit: "min/max", min: { unit: "px", absValue: 50 }, max: { unit: "max-c" } });
88
+ /** Default responsive collapse for a multi-column row: full columns on desktop/laptop,
89
+ * 2 columns on tablet, 1 column on mobile. `null` = keep the full column count. */
90
+ const ROW_COLLAPSE_DEFAULT = { bp1: null, bp2: null, bp3: 2, bp4: 1 };
91
+ /**
92
+ * Lay children out HORIZONTALLY — side by side in one grid row — the way real BuilderX
93
+ * pages build feature cards, category tiles, footer columns, 2-col hero, etc. The grid is
94
+ * `Nx1` with N equal `fr` columns and each child placed in its own column. Values go to
95
+ * `runtime` plus `__row`/`__cell` markers that finalizeForRender() reads to emit a
96
+ * RESPONSIVE grid per breakpoint (it auto-collapses to fewer columns on tablet/mobile so
97
+ * the row never renders as cramped slivers).
98
+ */
99
+ export function rowChildren(container, children, opts = {}) {
100
+ const cols = children.length || 1;
101
+ const colWidths = opts.colWidths && opts.colWidths.length === cols
102
+ ? opts.colWidths
103
+ : Array.from({ length: cols }, () => ({ unit: "fr", value: 1 }));
104
+ const collapse = { ...ROW_COLLAPSE_DEFAULT, ...(opts.collapse || {}) };
105
+ const columnGap = opts.columnGap ?? 24;
106
+ const rowGap = opts.rowGap ?? 24;
107
+ const meta = { cols, count: children.length, collapse, columnGap, rowGap };
108
+ container.runtime = container.runtime || {};
109
+ container.runtime.config = {
110
+ ...(container.runtime.config || {}),
111
+ grid: `${cols}x1`,
112
+ columns: colWidths,
113
+ rows: [ROW_PLACEHOLDER_ROW()],
114
+ columnGap,
115
+ rowGap,
116
+ heightUnit: "auto",
117
+ __row: meta,
118
+ };
119
+ children.forEach((child, i) => {
120
+ child.runtime = child.runtime || {};
121
+ child.runtime.config = {
122
+ ...(child.runtime.config || {}),
123
+ columnStart: i + 1,
124
+ columnEnd: i + 2,
125
+ rowStart: 1,
126
+ rowEnd: 2,
127
+ constraintX: (child.runtime.config && child.runtime.config.constraintX) || ["centerLeft"],
128
+ constraintY: (child.runtime.config && child.runtime.config.constraintY) || ["top"],
129
+ loaded: true,
130
+ __cell: { index: i, ...meta },
131
+ };
132
+ });
133
+ container.children = children;
134
+ return container;
135
+ }
136
+ /** Build a standalone multi-column ROW container from child specs (each laid side by side).
137
+ * Used by the new_row tool; inside new_section pass `layout:"row"` on a container spec. */
138
+ export function buildRow(childSpecs = [], opts = {}) {
139
+ const container = buildElement("container", opts.containerOpts || {});
140
+ const children = childSpecs.map((spec) => buildFromSpec(spec));
141
+ return rowChildren(container, children, opts);
142
+ }
143
+ /** How many columns a row shows at a given breakpoint (clamped to its real column count). */
144
+ function colsForBp(meta, bp) {
145
+ const c = meta.collapse ? meta.collapse[bp] : null;
146
+ const k = c == null ? meta.cols : Math.min(c, meta.cols);
147
+ return Math.max(1, k);
148
+ }
87
149
  /**
88
150
  * Build a ready-to-place section from a list of child specs.
89
151
  * Each spec: { type, opts?, children? } where children is a nested array of specs.
@@ -118,7 +180,19 @@ function buildFromSpec(spec) {
118
180
  const node = buildElement(spec.type, spec.opts || {});
119
181
  if (Array.isArray(spec.children) && spec.children.length) {
120
182
  const kids = spec.children.map((c) => buildFromSpec(c));
121
- stackChildren(node, kids);
183
+ // `layout:"row"` lays the children out side by side (responsive); default is a
184
+ // top-to-bottom vertical stack. rowGap/columnGap/colWidths/collapse tune the layout.
185
+ if (spec.layout === "row") {
186
+ rowChildren(node, kids, {
187
+ columnGap: spec.columnGap,
188
+ rowGap: spec.rowGap,
189
+ colWidths: spec.colWidths,
190
+ collapse: spec.collapse,
191
+ });
192
+ }
193
+ else {
194
+ stackChildren(node, kids, { rowGap: spec.rowGap });
195
+ }
122
196
  }
123
197
  return node;
124
198
  }
@@ -193,9 +267,14 @@ function expandNodeToBreakpoints(node) {
193
267
  const baseStyle = rt.style || {};
194
268
  const baseConfig = { ...(rt.config || {}), loaded: true };
195
269
  const isSection = node.type === "section";
270
+ const rowMeta = baseConfig.__row; // this node is a multi-column row container
271
+ const cellMeta = baseConfig.__cell; // this node is a cell inside a row
196
272
  for (const [bp, [minW]] of Object.entries(BREAKPOINTS)) {
197
273
  const style = clone(baseStyle);
198
274
  const config = clone(baseConfig);
275
+ // Internal build-time markers never get persisted.
276
+ delete config.__row;
277
+ delete config.__cell;
199
278
  if (isSection) {
200
279
  const g = genGridByBp(minW);
201
280
  const sectionRows = baseConfig.rows && baseConfig.rows.length ? clone(baseConfig.rows) : clone(g.rows);
@@ -204,6 +283,29 @@ function expandNodeToBreakpoints(node) {
204
283
  config.grid = `3x${sectionRows.length}`;
205
284
  config.heightUnit = config.heightUnit || "auto";
206
285
  }
286
+ // A multi-column row collapses to fewer columns on smaller screens so cards never
287
+ // shrink to slivers: recompute the grid + this container's columns per breakpoint.
288
+ if (rowMeta) {
289
+ const k = colsForBp(rowMeta, bp);
290
+ const nrows = Math.ceil(rowMeta.count / k);
291
+ config.columns =
292
+ k === rowMeta.cols && Array.isArray(baseConfig.columns) && baseConfig.columns.length === k
293
+ ? clone(baseConfig.columns)
294
+ : Array.from({ length: k }, () => ({ unit: "fr", value: 1 }));
295
+ config.rows = Array.from({ length: nrows }, () => ROW_PLACEHOLDER_ROW());
296
+ config.grid = `${k}x${nrows}`;
297
+ config.heightUnit = config.heightUnit || "auto";
298
+ }
299
+ // A cell repositions itself into the collapsed grid (wraps to a new row as needed).
300
+ if (cellMeta) {
301
+ const k = colsForBp(cellMeta, bp);
302
+ const c = cellMeta.index % k;
303
+ const r = Math.floor(cellMeta.index / k);
304
+ config.columnStart = c + 1;
305
+ config.columnEnd = c + 2;
306
+ config.rowStart = r + 1;
307
+ config.rowEnd = r + 2;
308
+ }
207
309
  node[bp] = { style, config };
208
310
  }
209
311
  delete node.runtime;