diff --git a/.gitignore b/.gitignore index 87f30c737..5f28a2e2f 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ package-lock.json temp _data db +chrome-profile diff --git a/uniquely/com-api/src/lib/config.ts b/uniquely/com-api/src/lib/config.ts index b92975cdf..71924d086 100644 --- a/uniquely/com-api/src/lib/config.ts +++ b/uniquely/com-api/src/lib/config.ts @@ -18,7 +18,7 @@ export const config = { storageClient: { host: process.env.STORAGE_HOST ?? '127.0.0.1', port: process.env.STORAGE_PORT != null ? +process.env.STORAGE_PORT : 9000, - token: process.env.STORAGE_TOKEN ?? 'YOUR_SECRET', + token: process.env.STORAGE_TOKEN ?? 'YOUR_SECRET_TOKEN', }, token: { diff --git a/uniquely/com-pwa/src/config.ts b/uniquely/com-pwa/src/config.ts index 339627c4c..2c8aa90b2 100644 --- a/uniquely/com-pwa/src/config.ts +++ b/uniquely/com-pwa/src/config.ts @@ -30,7 +30,7 @@ export const config = { newOrder: apiBaseUrl + 'order', // admin access - userList: apiBaseUrl + 'user-list', + adminUserListIncOrder: apiBaseUrl + 'admin/user-list-inc-order', }, fetchContextOptions: >{ diff --git a/uniquely/com-pwa/src/content/l18e-fa.json b/uniquely/com-pwa/src/content/l18e-fa.json index 181bbfdc4..29bdde65e 100644 --- a/uniquely/com-pwa/src/content/l18e-fa.json +++ b/uniquely/com-pwa/src/content/l18e-fa.json @@ -44,7 +44,17 @@ "page_order_order_not_valid_message": "لطفا مقادیر سفارش رو بازبینی و اصلاح کنید.", "order_summary_palette_price": "هزینه پالت (${paletteCount} عدد)", - "page_order_tracking_headline": "پیگیری سفارش", + "page_admin_order_list_headline": "لیست سفارشات کاربران", + "page_admin_order_list_phone_number": "شماره تلفن", + "page_admin_order_list_province": "استان", + "page_admin_order_list_city": "شهر", + "page_admin_order_list_order_id": "کد سفارش", + "page_admin_order_list_order_list": "لیست سفارشات", + "page_admin_order_list_order_status": "وضعیت سفارش", + "page_admin_order_list_order_date": "تاریخ ثبت سفارش", + "page_admin_order_list_empty_order_list": "سفارشی ثبت نشده است.", + + "page_admin_order_list_reloading_failed": "بارگذاری با خطا روبرو شد، لطفا مجدد تلاش کنید.", "page_sign_in_headline": "مدیریت سفارشات سافیت", "page_sign_sign_in_failed": "ورود با خطا روبرو شد! لطفا دوباره امتحان کنید.", diff --git a/uniquely/com-pwa/src/manager/buttons.ts b/uniquely/com-pwa/src/manager/buttons.ts index 67d9643c9..5f119c30b 100644 --- a/uniquely/com-pwa/src/manager/buttons.ts +++ b/uniquely/com-pwa/src/manager/buttons.ts @@ -24,6 +24,11 @@ export const buttons = { icon: 'reload-outline', }, + reloadAdminOrderListStorage: { + icon: 'reload-outline', + flipRtl: true, + clickSignalId: 'reload_admin_order_list_storage', + }, reloadOrderStorage: { icon: 'reload-outline', flipRtl: true, diff --git a/uniquely/com-pwa/src/manager/context-provider/user-list-storage.ts b/uniquely/com-pwa/src/manager/context-provider/user-list-storage.ts index 767686d6f..48fb02188 100644 --- a/uniquely/com-pwa/src/manager/context-provider/user-list-storage.ts +++ b/uniquely/com-pwa/src/manager/context-provider/user-list-storage.ts @@ -3,19 +3,20 @@ import {serverContextConsumer} from '@alwatr/context'; import {userProfileContextConsumer} from './user.js'; import {config} from '../../config.js'; -import type {AlwatrDocumentStorage} from '@alwatr/type'; -import type {ComUser} from '@alwatr/type/customer-order-management.js'; +import type {AlwatrServiceResponseSuccessWithMeta} from '@alwatr/type'; +import type {ComUserIncOrder} from '@alwatr/type/customer-order-management.js'; -export const userListStorageContextConsumer = serverContextConsumer>( - 'user_list_storage_context', - { - ...config.fetchContextOptions, - url: config.serverContext.userList, - }, -); +export const userListIncOrderStorageContextConsumer = + serverContextConsumer>>( + 'admin_order_list_storage_context', + { + ...config.fetchContextOptions, + url: config.serverContext.adminUserListIncOrder, + }, + ); userProfileContextConsumer.subscribe((userProfile) => { - userListStorageContextConsumer.request({ + userListIncOrderStorageContextConsumer.request({ userAuth: { id: userProfile.id, token: userProfile.token!, diff --git a/uniquely/com-pwa/src/ui/alwatr-pwa.ts b/uniquely/com-pwa/src/ui/alwatr-pwa.ts index 4d07bf959..5487e40ce 100644 --- a/uniquely/com-pwa/src/ui/alwatr-pwa.ts +++ b/uniquely/com-pwa/src/ui/alwatr-pwa.ts @@ -33,7 +33,8 @@ class AlwatrPwa extends AlwatrPwaElement { 'home': this._renderPageHome, '_404': this._renderPage404, 'order-list': this._renderPageOrderList, - 'user-list': this._renderPageUserList, + 'admin-order-list': this._renderPageAdminOrderList, + 'admin-order': this._renderPageAdminOrder, 'order': this._renderPageOrder, 'sign-in': this._renderPageSignIn, 's': this._saveLinkPass, @@ -61,10 +62,21 @@ class AlwatrPwa extends AlwatrPwaElement { return html`...`; } - protected _renderPageUserList(): unknown { - import('./page/user-list.js'); + protected _renderPageAdminOrderList(): unknown { + import('./page/admin-order-list.js'); topAppBarContextProvider.setValue({headlineKey: 'loading'}); - return html`...`; + return html`...`; + } + + protected _renderPageAdminOrder(routeContext: RouteContext): unknown { + import('./page/admin-order.js'); + topAppBarContextProvider.setValue({headlineKey: 'loading'}); + return html`...`; } protected _renderPageOrder(routeContext: RouteContext): unknown { @@ -101,11 +113,7 @@ class AlwatrPwa extends AlwatrPwaElement { return; } // else - if ( - routeId !== 'sign-in' && - routeId !== 's' && - routeId !== '' - ) { + if (routeId !== 'sign-in' && routeId !== 's' && routeId !== '') { redirect({sectionList: ['sign-in']}); } } diff --git a/uniquely/com-pwa/src/ui/page/admin-order-list.ts b/uniquely/com-pwa/src/ui/page/admin-order-list.ts new file mode 100644 index 000000000..5ddfdd3ba --- /dev/null +++ b/uniquely/com-pwa/src/ui/page/admin-order-list.ts @@ -0,0 +1,170 @@ +import { + customElement, + css, + html, + LocalizeMixin, + SignalMixin, + AlwatrBaseElement, + UnresolvedMixin, + state, + ScheduleUpdateToFrameMixin, + when, + mapObject, + type PropertyValues, +} from '@alwatr/element'; +import {message} from '@alwatr/i18n'; +import {eventListener} from '@alwatr/signal'; +import '@alwatr/ui-kit/button/button.js'; +import '@alwatr/ui-kit/card/icon-box.js'; +import '@alwatr/ui-kit/card/surface.js'; + +import {buttons} from '../../manager/buttons.js'; +import {userListIncOrderStorageContextConsumer} from '../../manager/context-provider/user-list-storage.js'; +import {topAppBarContextProvider} from '../../manager/context.js'; +import '../stuff/user-inc-order-box.js'; + +import type {IconBoxContent} from '@alwatr/ui-kit/card/icon-box.js'; + +declare global { + interface HTMLElementTagNameMap { + 'alwatr-page-admin-order-list': AlwatrPageAdminOrderList; + } +} + +/** + * List of all users. + */ +@customElement('alwatr-page-admin-order-list') +export class AlwatrPageAdminOrderList extends ScheduleUpdateToFrameMixin( + UnresolvedMixin(LocalizeMixin(SignalMixin(AlwatrBaseElement))), +) { + static override styles = css` + :host { + display: flex; + flex-direction: column; + padding: calc(2 * var(--sys-spacing-track)); + box-sizing: border-box; + min-height: 100%; + gap: var(--sys-spacing-track); + transform: opacity var(--sys-motion-duration-small); + } + + :host([state='reloading']) { + opacity: var(--sys-surface-reloading-opacity); + } + + .reloading-failed { + margin-bottom: var(--sys-spacing-track); + } + `; + + @state() + gotState = userListIncOrderStorageContextConsumer.getState().target; + + override connectedCallback(): void { + super.connectedCallback(); + + this._addSignalListeners(userListIncOrderStorageContextConsumer.subscribe(() => { + this.gotState = userListIncOrderStorageContextConsumer.getState().target; + }, {receivePrevious: 'NextCycle'})); + + this._addSignalListeners(eventListener.subscribe(buttons.retry.clickSignalId, () => { + userListIncOrderStorageContextConsumer.request(); + })); + this._addSignalListeners(eventListener.subscribe(buttons.reloadAdminOrderListStorage.clickSignalId, () => { + userListIncOrderStorageContextConsumer.request(); + })); + } + + protected override update(changedProperties: PropertyValues): void { + super.update(changedProperties); + if (changedProperties.has('gotState')) { + this.setAttribute('state', this.gotState); + } + } + + override render(): unknown { + this._logger.logMethod?.('render'); + return userListIncOrderStorageContextConsumer.fsm.render({ + initial: 'onlineLoading', + offlineLoading: 'onlineLoading', + onlineLoading: this._renderStateLoading, + loadingFailed: this._renderStateLoadingFailed, + reloadingFailed: 'complete', + reloading: 'complete', + complete: this._renderStateComplete, + }, this); + } + + protected _renderStateLoading(): unknown { + this._logger.logMethod?.('_renderStateLoading'); + + topAppBarContextProvider.setValue({ + headlineKey: 'loading', + startIcon: buttons.backToHome, + }); + const content: IconBoxContent = { + tinted: 1, + icon: 'cloud-download-outline', + headline: message('loading'), + }; + return html``; + } + + protected _renderStateLoadingFailed(): unknown { + this._logger.logMethod?.('_renderStateLoadingFailed'); + + topAppBarContextProvider.setValue({ + headlineKey: 'page_order_list_headline', + startIcon: buttons.backToHome, + endIconList: [buttons.reloadOrderStorage], + }); + const content: IconBoxContent = { + icon: 'cloud-offline-outline', + tinted: 1, + headline: message('fetch_failed_headline'), + description: message('fetch_failed_description'), + }; + return html` + +
+ +
+ `; + } + + protected _renderStateComplete(): unknown { + this._logger.logMethod?.('_renderStateComplete'); + + topAppBarContextProvider.setValue({ + headlineKey: 'page_admin_order_list_headline', + startIcon: buttons.backToHome, + endIconList: [ + buttons.newOrder, {...buttons.reloadAdminOrderListStorage, disabled: this.gotState === 'reloading'}, + ], + }); + return html` + ${when(this.gotState === 'reloadingFailed', this._render_reloadingFailed)} + ${this._render_usersList()} + `; + } + + private _render_usersList(): unknown { + const userStorage = userListIncOrderStorageContextConsumer.getResponse(); + this._logger.logMethodArgs?.('renderUsersList', {userStorage}); + + return mapObject( + this, + userStorage?.data, + (user) => { + return html``; + }, + ); + } + + private _render_reloadingFailed(): unknown { + return html` + ${message('page_admin_order_list_reloading_failed')} + `; + } +} diff --git a/uniquely/com-pwa/src/ui/page/admin-order.ts b/uniquely/com-pwa/src/ui/page/admin-order.ts new file mode 100644 index 000000000..bb8a6dbe6 --- /dev/null +++ b/uniquely/com-pwa/src/ui/page/admin-order.ts @@ -0,0 +1,484 @@ +import { + AlwatrBaseElement, + css, + CSSResultGroup, + customElement, + html, + LocalizeMixin, + mapIterable, + nothing, + property, + PropertyValues, + SignalMixin, + state, + UnresolvedMixin, +} from '@alwatr/element'; +import {message, number, replaceNumber} from '@alwatr/i18n'; +import '@alwatr/icon'; +import {calcDiscount} from '@alwatr/math'; +import {redirect} from '@alwatr/router'; +import {eventListener} from '@alwatr/signal'; +import '@alwatr/ui-kit/card/icon-box.js'; +import '@alwatr/ui-kit/card/surface.js'; + +import {config} from '../../config.js'; +import {buttons} from '../../manager/buttons.js'; +import {productStorageContextConsumer} from '../../manager/context-provider/product-storage.js'; +import {userListIncOrderStorageContextConsumer} from '../../manager/context-provider/user-list-storage.js'; +import {scrollToTopCommand, topAppBarContextProvider} from '../../manager/context.js'; +import '../stuff/order-status-box.js'; + +import type {AlwatrDocumentStorage} from '@alwatr/type'; +import type {Order, OrderDraft, OrderItem, OrderShippingInfo, Product} from '@alwatr/type/customer-order-management.js'; +import type {IconBoxContent} from '@alwatr/ui-kit/card/icon-box.js'; + +declare global { + interface HTMLElementTagNameMap { + 'alwatr-page-admin-order': AlwatrPageAdminOrder; + } +} + +/** + * Alwatr Customer Order Management Order Form Page + */ +@customElement('alwatr-page-admin-order') +export class AlwatrPageAdminOrder extends UnresolvedMixin(LocalizeMixin(SignalMixin(AlwatrBaseElement))) { + static override styles: CSSResultGroup = css` + :host { + display: flex; + flex-direction: column; + padding: calc(2 * var(--sys-spacing-track)); + box-sizing: border-box; + min-height: 100%; + gap: var(--sys-spacing-track); + } + + :host([state='reloading']) > * { + opacity: var(--sys-surface-disabled-opacity); + } + + alwatr-surface { + --_surface-color-on: var(--sys-color-on-surface-variant-hsl); + } + + .product-item { + display: flex; + flex-direction: row; + gap: var(--sys-spacing-track); + } + + .product-item > img { + display: block; + width: calc(6 * var(--sys-spacing-track)); + border-radius: var(--sys-radius-small); + align-self: flex-start; + } + + .detail-container { + flex-grow: 1; + } + + .detail-container > * { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--sys-spacing-track); + } + + .detail-container > *:last-child { + margin-bottom: 0; + } + + .submit-container { + text-align: end; + } + + /* ----- */ + .number-field { + --_surface-color-on: var(--sys-color-on-surface-variant-hsl); + /* --_surface-color-bg: var(--sys-color-surface-variant-hsl); */ + display: flex; + align-items: center; + padding: 0; + font-family: var(--sys-typescale-body-large-font-family-name); + font-weight: var(--sys-typescale-body-large-font-weight); + font-size: var(--sys-typescale-body-large-font-size); + letter-spacing: var(--sys-typescale-body-large-letter-spacing); + line-height: var(--sys-typescale-body-large-line-height); + border-radius: var(--sys-radius-xsmall); + text-align: center; + box-shadow: none; + border-bottom: 1px solid var(--sys-color-outline); + border-radius: var(--sys-radius-xsmall) var(--sys-radius-xsmall) 0 0; + width: calc(20 * var(--sys-spacing-track)); + margin-right: auto; + } + alwatr-text-field { + display: block; + padding: 0; + width: 100%; + flex-grow: 1; + border-radius: inherit; + } + + /* So not group these selectors! */ + input::placeholder { + font: inherit; + color: var(--sys-color-on-surface-variant); + } + input::-webkit-input-placeholder { + font: inherit; + color: var(--sys-color-on-surface-variant); + } + input::-moz-placeholder { + font: inherit; + color: var(--sys-color-on-surface-variant); + } + + input[type='number'] { + -moz-appearance: textfield; + } + + input::-webkit-outer-spin-button, + input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + + .bold-text { + font-weight: var(--ref-font-weight-bold); + } + + [hidden] { + display: none; + } + `; + + @state() + gotState = userListIncOrderStorageContextConsumer.getState().target; + + @property({type: String}) + orderId?: string; + + @property({type: String}) + userId?: string; + + override connectedCallback(): void { + super.connectedCallback(); + + this._addSignalListeners(eventListener.subscribe(buttons.backToOrderList.clickSignalId, (): void => { + redirect({sectionList: ['admin-order-list']}); + })); + } + + protected override update(changedProperties: PropertyValues): void { + super.update(changedProperties); + if (changedProperties.has('orderId')) { + scrollToTopCommand.request({smooth: true}); + } + } + + protected override render(): unknown { + this._logger.logMethod?.('render'); + return userListIncOrderStorageContextConsumer.fsm.render({ + initial: 'onlineLoading', + offlineLoading: 'onlineLoading', + onlineLoading: this._renderStateLoading, + loadingFailed: this._renderStateLoadingFailed, + reloadingFailed: 'complete', + reloading: 'complete', + complete: this._renderStateComplete, + }, this); + } + + protected _renderStateLoading(): unknown { + this._logger.logMethod?.('_renderStateLoading'); + + topAppBarContextProvider.setValue({ + headlineKey: 'loading', + startIcon: buttons.backToHome, + }); + const content: IconBoxContent = { + tinted: 1, + icon: 'cloud-download-outline', + headline: message('loading'), + }; + return html``; + } + + protected _renderStateLoadingFailed(): unknown { + this._logger.logMethod?.('_renderStateLoadingFailed'); + + topAppBarContextProvider.setValue({ + headlineKey: 'page_order_list_headline', + startIcon: buttons.backToHome, + endIconList: [buttons.reloadOrderStorage], + }); + const content: IconBoxContent = { + icon: 'cloud-offline-outline', + tinted: 1, + headline: message('fetch_failed_headline'), + description: message('fetch_failed_description'), + }; + + return html``; + } + + protected _render_notFound(): unknown { + this._logger.logMethod?.('_render_notFound'); + + topAppBarContextProvider.setValue({ + headlineKey: 'page_order_list_headline', + startIcon: buttons.backToOrderList, + }); + const content: IconBoxContent = { + headline: message('page_order_detail_not_found'), + icon: 'close', + tinted: 1, + }; + return html``; + } + + protected _renderStateComplete(): unknown { + this._logger.logMethod?.('_renderStateComplete'); + + topAppBarContextProvider.setValue({ + headlineKey: 'page_order_list_headline', + startIcon: buttons.backToOrderList, + }); + + const productStorage = productStorageContextConsumer.getResponse(); + + if (this.userId == null || this.orderId == null || productStorage == null) { + return this._render_notFound(); + } + + const order = userListIncOrderStorageContextConsumer.getResponse()?.data[this.userId]?.orderList[this.orderId]; + if (order == null) { + return this._render_notFound(); + } + + return [ + this._render_status(order), + this._render_itemList(order.itemList, productStorage), + this._render_shippingInfo(order.shippingInfo), + this._render_summary(order), + ]; + } + + protected _render_status(order: Order | OrderDraft): unknown { + this._logger.logMethod?.('_render_status'); + return html``; + } + + protected _render_itemList( + itemList: Array, + productStorage: AlwatrDocumentStorage | null | undefined, + ): unknown { + this._logger.logMethod?.('_render_itemList'); + + return mapIterable(this, itemList, (item) => { + const product = productStorage?.data[item.productId]; + if (product == null) { + this._logger.error('itemDetailTemplate', 'product_not_found', {productId: item.productId}); + return html`${message('order_item_not_exist')}`; + } + + return html` + +
+
${product.title.fa}
+
+ ${message('order_item_price')}: + + ${number(item.marketPrice)} + + +
+
+ ${message('order_item_final_price')}: + + ${number(item.agencyPrice)} + + +
+
+ ${message('order_item_qty_m2')}: + ${number(item.qty * config.order.factor.box2m2)} +
+
+ ${message('order_item_qty_tile')}: + + ${number(item.qty * config.order.factor.box2tile)} + + +
+
+ ${message('order_item_qty_box')}: + + ${number(item.qty)} + + +
+
+ ${message('order_item_final_total_price')}: + + ${number(item.qty * config.order.factor.box2m2 * item.agencyPrice)} + + +
+
+ ${message('order_item_total_price')}: + + ${number(item.qty * config.order.factor.box2m2 * item.marketPrice)} + + +
+
+ ${message('order_item_discount')}: + + + (٪${number(calcDiscount(item.marketPrice, item.agencyPrice))}) + ${number(item.qty * config.order.factor.box2m2 * (item.marketPrice - item.agencyPrice))} + + + +
+
+
`; + }); + } + + protected _render_shippingInfo(shippingInfo?: Partial): unknown { + this._logger.logMethod?.('_render_shippingInfo'); + + const nullStr = '…' as const; + + // if (!shippingInfo) { + // return html`
+ // } + + return html` +
+
+ ${message('order_shipping_recipient_name_title')}: + ${shippingInfo?.recipientName || nullStr} +
+
+ ${message('order_shipping_recipient_national_code_title')}: + ${replaceNumber(shippingInfo?.recipientNationalCode || nullStr)} +
+
+ ${message('order_shipping_address_title')}: + ${replaceNumber(shippingInfo?.address || nullStr)} +
+
+ ${message('order_shipping_car_type_title')}: + + ${shippingInfo?.carType ? message('order_shipping_car_type_key_' + shippingInfo?.carType) : nullStr} + +
+
+ ${message('order_shipping_lading_type_title')}: + + ${shippingInfo?.ladingType + ? message( + 'order_shipping_lading_type_key_' + + (shippingInfo?.carType != 'trailer_truck' ? 'hand' : shippingInfo?.ladingType), + ) + : nullStr} + +
+
+ ${message('order_shipping_time_period_title')}: + + ${shippingInfo?.timePeriod ? message('order_shipping_time_period_key_' + shippingInfo.timePeriod) : nullStr} + +
+
+ ${message('order_shipping_description_title')}: + ${shippingInfo?.description} +
+
+
`; + } + + protected _render_summary(order: Order): unknown { + this._logger.logMethod?.('_render_summary'); + if (!order.itemList?.length) return nothing; + + return html` +
+
+ ${message('order_summary_total_price')}: + + ${number(order.subTotalMarket)} + + +
+
+ ${message('order_summary_total_final_price')}: + + ${number(order.subTotalAgency)} + + +
+
+ ${message('order_summary_discount')}: + + + (٪${number(calcDiscount(order.subTotalMarket, order.subTotalAgency))}) + ${number(order.subTotalMarket - order.subTotalAgency)} + + + +
+
+ ${message('order_summary_lading_price').replace( + '${carCount}', + number(order.ladingFee / (config.order.lading[order.shippingInfo.carType!]?.fee ?? order.ladingFee)), + )}: + + + ${number(order.ladingFee)} + + +
+
+ + ${message('order_summary_palette_price').replace( + '${paletteCount}', + number(order.palletCost / config.order.pallet.price), + )}: + + + ${number(order.palletCost)} + + +
+
+ ${message('order_shipping_shipment_price_title')}: + ${message('order_shipping_shipment_price_value')} +
+
+ ${message('order_summary_discount_after_lading_price')}: + + + (٪${number(calcDiscount(order.subTotalMarket, order.subTotalAgency + order.totalShippingFee))}) + ${number(order.subTotalMarket - order.subTotalAgency - order.totalShippingFee)} + + + +
+
+ ${message('order_summary_final_total_price')}: + + ${number(order.subTotalAgency + order.ladingFee + order.palletCost)} + + +
+
+
`; + } +} diff --git a/uniquely/com-pwa/src/ui/page/order.ts b/uniquely/com-pwa/src/ui/page/order.ts index 9dc7d01bb..909322000 100644 --- a/uniquely/com-pwa/src/ui/page/order.ts +++ b/uniquely/com-pwa/src/ui/page/order.ts @@ -43,7 +43,7 @@ import type {AlwatrTextField} from '@alwatr/ui-kit/text-field/text-field.js'; declare global { interface HTMLElementTagNameMap { - 'alwatr-page-order': AlwatrPageNewOrder; + 'alwatr-page-order': AlwatrPageOrder; } } @@ -51,7 +51,7 @@ declare global { * Alwatr Customer Order Management Order Form Page */ @customElement('alwatr-page-order') -export class AlwatrPageNewOrder extends UnresolvedMixin(LocalizeMixin(SignalMixin(AlwatrBaseElement))) { +export class AlwatrPageOrder extends UnresolvedMixin(LocalizeMixin(SignalMixin(AlwatrBaseElement))) { static override styles: CSSResultGroup = css` :host { display: flex; diff --git a/uniquely/com-pwa/src/ui/page/user-list.ts b/uniquely/com-pwa/src/ui/page/user-list.ts deleted file mode 100644 index 651e0b582..000000000 --- a/uniquely/com-pwa/src/ui/page/user-list.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { - customElement, - css, - html, - LocalizeMixin, - SignalMixin, - AlwatrBaseElement, - UnresolvedMixin, - state, - ScheduleUpdateToFrameMixin, - guard, - when, - type PropertyValues, - mapObject, -} from '@alwatr/element'; -import {message} from '@alwatr/i18n'; -import {eventListener} from '@alwatr/signal'; -import '@alwatr/ui-kit/button/button.js'; -import '@alwatr/ui-kit/card/icon-box.js'; -import '@alwatr/ui-kit/card/surface.js'; - -import {buttons} from '../../manager/buttons.js'; -import {userListStorageContextConsumer} from '../../manager/context-provider/user-list-storage.js'; -import {topAppBarContextProvider} from '../../manager/context.js'; -import '../stuff/order-status-box.js'; -import '../stuff/user-info-box.js'; - -import type {IconBoxContent} from '@alwatr/ui-kit/card/icon-box.js'; - -declare global { - interface HTMLElementTagNameMap { - 'alwatr-page-user-list': AlwatrPageUserList; - } -} - -/** - * List of all users. - */ -@customElement('alwatr-page-user-list') -export class AlwatrPageUserList extends ScheduleUpdateToFrameMixin( - UnresolvedMixin(LocalizeMixin(SignalMixin(AlwatrBaseElement))), -) { - static override styles = css` - :host { - display: flex; - flex-direction: column; - padding: calc(2 * var(--sys-spacing-track)); - box-sizing: border-box; - min-height: 100%; - gap: var(--sys-spacing-track); - transform: opacity var(--sys-motion-duration-small); - } - - :host([state='reloading']) { - opacity: var(--sys-surface-reloading-opacity); - } - - alwatr-order-status-box { - margin-bottom: var(--sys-spacing-track); - } - - .reloadingFailed { - margin-bottom: var(--sys-spacing-track); - } - `; - - @state() - gotState = userListStorageContextConsumer.getState().target; - - override connectedCallback(): void { - super.connectedCallback(); - - // prettier-ignore - this._addSignalListeners(userListStorageContextConsumer.subscribe(() => { - this.gotState = userListStorageContextConsumer.getState().target; - }, {receivePrevious: 'NextCycle'})); - - this._addSignalListeners( - eventListener.subscribe(buttons.retry.clickSignalId, () => { - userListStorageContextConsumer.request(); - }), - ); - } - - protected override update(changedProperties: PropertyValues): void { - super.update(changedProperties); - if (changedProperties.has('gotState')) { - this.setAttribute('state', this.gotState); - } - } - - override render(): unknown { - this._logger.logMethod?.('render'); - return userListStorageContextConsumer.fsm.render({ - initial: 'onlineLoading', - offlineLoading: 'onlineLoading', - onlineLoading: () => { - topAppBarContextProvider.setValue({ - headlineKey: 'loading', - startIcon: buttons.backToHome, - }); - const content: IconBoxContent = { - tinted: 1, - icon: 'cloud-download-outline', - headline: message('loading'), - }; - return html``; - }, - - loadingFailed: () => { - topAppBarContextProvider.setValue({ - headlineKey: 'page_order_list_headline', - startIcon: buttons.backToHome, - endIconList: [buttons.reloadOrderStorage], - }); - const content: IconBoxContent = { - icon: 'cloud-offline-outline', - tinted: 1, - headline: message('fetch_failed_headline'), - description: message('fetch_failed_description'), - }; - return html` - -
- -
- `; - }, - - reloadingFailed: 'complete', - reloading: 'complete', - complete: () => { - topAppBarContextProvider.setValue({ - headlineKey: 'page_order_list_headline', - startIcon: buttons.backToHome, - endIconList: [buttons.newOrder, {...buttons.reloadOrderStorage, disabled: this.gotState === 'reloading'}], - }); - return html` - ${when(this.gotState === 'reloadingFailed', this._renderReloadingFailed)} - ${guard(userListStorageContextConsumer.getResponse()?.meta.lastUpdated, () => this._renderUsersList())} - `; - }, - }); - } - - private _renderUsersList(): unknown { - const userStorage = userListStorageContextConsumer.getResponse(); - this._logger.logMethodArgs?.('_renderUsersList', {userStorage}); - - return mapObject( - this, - userStorage?.data, - (user) => html``, - ); - } - - private _renderReloadingFailed(): unknown { - return html` - ${message('page_user_list_reloading_failed')} - `; - } -} diff --git a/uniquely/com-pwa/src/ui/stuff/user-inc-order-box.ts b/uniquely/com-pwa/src/ui/stuff/user-inc-order-box.ts new file mode 100644 index 000000000..af7f3fc27 --- /dev/null +++ b/uniquely/com-pwa/src/ui/stuff/user-inc-order-box.ts @@ -0,0 +1,140 @@ +import { + AlwatrBaseElement, + LocalizeMixin, + SignalMixin, + css, + customElement, + html, + mapObject, + property, +} from '@alwatr/element'; +import {date, message} from '@alwatr/i18n'; +import {url} from '@alwatr/router'; + +import type {ComUserIncOrder, Order} from '@alwatr/type/customer-order-management.js'; +import type {IconBoxContent} from '@alwatr/ui-kit/card/icon-box.js'; + +declare global { + interface HTMLElementTagNameMap { + 'alwatr-user-inc-order-box': alwatrUserIncOrderBox; + } +} + +/** + * Alwatr User Include Order Box Element. + */ +@customElement('alwatr-user-inc-order-box') +export class alwatrUserIncOrderBox extends LocalizeMixin(SignalMixin(AlwatrBaseElement)) { + static override styles = css` + :host { + display: block; + } + + .detail-container { + flex-grow: 1; + } + + .detail-container > * { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--sys-spacing-track); + } + + .detail-container > *:last-child { + margin-bottom: 0; + } + + .order-container { + padding: 0; + } + + .order-info { + padding: calc(2 * var(--sys-spacing-track)); + cursor: pointer; + } + + .order-info:hover { + background-color: var(--sys-color-surface-variant); + color: var(--sys-color-on-surface-variant); + } + + .empty-order { + padding: calc(2 * var(--sys-spacing-track)); + } + + a { + display: block; + color: inherit; + text-decoration: none; + } + `; + + @property() + userIncOrder?: ComUserIncOrder; + + override render(): unknown { + this._logger.logMethod?.('render'); + const userProfileIconBox: IconBoxContent = { + tinted: 1, + headline: this.userIncOrder?.fullName ?? '', + icon: 'person-circle-outline', + }; + + return html` + +
+
+ ${message('page_admin_order_list_phone_number')}: + +${this.userIncOrder?.phoneNumber} +
+
+ ${message('page_admin_order_list_province')}: + ${this.userIncOrder?.province} +
+
+ ${message('page_admin_order_list_city')}: + ${this.userIncOrder?.city} +
+
+ ${this._renderOrderBox()} +
+ `; + } + + protected _renderOrderBox(): unknown { + let orderList; + if (Object.keys(this.userIncOrder!.orderList).length === 0) { + orderList = html`${message('page_admin_order_list_empty_order_list')}`; + } + else { + orderList = mapObject(this, this.userIncOrder!.orderList, (order) => this._renderOrderInfo(order)); + } + + return html` +

${message('page_admin_order_list_order_list')}:

+ ${orderList} + `; + } + + protected _renderOrderInfo(order: Order): unknown { + return html` + +
+ ${message('page_admin_order_list_order_id')}: + ${order.id} +
+
+ ${message('page_admin_order_list_order_date')}: + ${order.meta?.created ? date(order.meta?.created) : ''} +
+
+ ${message('page_admin_order_list_order_status')}: + ${message('order_status_' + order.status)} +
+
+ `; + } +} diff --git a/uniquely/com-pwa/src/ui/stuff/user-info-box.ts b/uniquely/com-pwa/src/ui/stuff/user-info-box.ts deleted file mode 100644 index 95893ffbb..000000000 --- a/uniquely/com-pwa/src/ui/stuff/user-info-box.ts +++ /dev/null @@ -1,152 +0,0 @@ -import {serverContextConsumer} from '@alwatr/context'; -import { - customElement, - AlwatrBaseElement, - html, - property, - css, - SignalMixin, - LocalizeMixin, - mapObject, - when, - type PropertyValues, -} from '@alwatr/element'; -import {message} from '@alwatr/i18n'; -import '@alwatr/ui-kit/button/icon-button.js'; -import '@alwatr/ui-kit/card/icon-box.js'; - - -import {config} from '../../config.js'; -import {buttons} from '../../manager/buttons.js'; - -import type {AlwatrDocumentStorage} from '@alwatr/type'; -import type {ComUser, Order} from '@alwatr/type/customer-order-management.js'; -import type {IconButtonContent} from '@alwatr/ui-kit/button/icon-button.js'; -import type {IconBoxContent} from '@alwatr/ui-kit/card/icon-box.js'; - -declare global { - interface HTMLElementTagNameMap { - 'alwatr-user-info-box': AlwatrUserInfoBox; - } -} - -/** - * Alwatr User Item Box Element. - */ -@customElement('alwatr-user-info-box') -export class AlwatrUserInfoBox extends LocalizeMixin(SignalMixin(AlwatrBaseElement)) { - static override styles = css` - :host { - display: block; - } - - :host([state='reloading']) { - opacity: var(--sys-surface-reloading-opacity); - } - `; - - @property({attribute: false}) - user: Partial = {}; - - protected orderListStorageContextConsumer = serverContextConsumer>( - 'order_list_storage_context_' + this.user.id, - { - url: config.serverContext.userOrderList, - }, - ); - - override connectedCallback(): void { - // prettier-ignore - this._addSignalListeners(this.orderListStorageContextConsumer.subscribe(() => { - const fsmStorageState = this.orderListStorageContextConsumer.getState().target; - const hasReloadingAttribute = this.hasAttribute('reloading'); - - if ((fsmStorageState === 'complete' || fsmStorageState === 'reloadingFailed') && hasReloadingAttribute) { - this.removeAttribute('reloading'); - return; - } - - if (fsmStorageState === 'reloading' && !hasReloadingAttribute) { - this.setAttribute('reloading', ''); - } - }, {receivePrevious: 'NextCycle'})); - } - - protected override shouldUpdate(changedProperties: PropertyValues): boolean { - return super.shouldUpdate(changedProperties) && this.user != null; - } - - override render(): unknown { - this._logger.logMethod?.('render'); - - return this.orderListStorageContextConsumer.fsm.render({ - initial: 'onlineLoading', - offlineLoading: 'onlineLoading', - onlineLoading: () => { - const content: IconBoxContent = { - tinted: 1, - icon: 'cloud-download-outline', - headline: message('loading'), - }; - return html``; - }, - loadingFailed: () => { - const content: IconBoxContent = { - icon: 'cloud-offline-outline', - tinted: 1, - headline: message('fetch_failed_headline'), - description: message('fetch_failed_description'), - }; - return html` - -
- -
- `; - }, - reloadingFailed: this._renderReloadingFailed, - reloading: 'complete', - complete: this._renderUserOrders, - }); - } - - private _renderUserOrders(): unknown { - this._logger.logMethod?.('_renderUserOrders'); - - return html` - -
- ${mapObject( - this, - this.orderListStorageContextConsumer.getResponse()?.data, - (order) => html``, - )} -
- `; - } - - private _renderUserInfo(): unknown { - this._logger.logMethod?.('_renderUserInfo'); - - const userProfileIconButton: IconButtonContent = { - icon: 'person-circle-outline', - }; - - return html` - - ${this.user.fullName} - ${this.user.phoneNumber} - ${this.user.province}${this.user!.city} - ${when(this.orderListStorageContextConsumer.getState().target !== 'complete', () => html`...`)} - ${when( - this.orderListStorageContextConsumer.getState().target === 'complete', - () => html`${Object.keys(this.orderListStorageContextConsumer.getResponse()?.data ?? {}).length}`, - )} - `; - } - - private _renderReloadingFailed(): unknown { - this._logger.logMethod?.('_renderReloadingFailed'); - return html` ${message('user_order_list_reloading_failed')} `; - } -}