| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- 'use client'
- import { useEffect, useMemo, useState } from 'react'
- import { useFormContext, useWatch } from 'react-hook-form'
- import Field from './_field'
- import { View, Row } from 'app/design/view'
- import { Text } from 'app/design/typography'
- import { Input } from 'app/design/controls'
- import CheckBox from 'app/ui/atoms/checkbox'
- import Loading from 'app/ui/atoms/loading'
- import { useFetch } from 'app/lib/hooks/use-fetch'
- import { useTranslation } from 'react-i18next'
- function resolveItemsUrl(requestUrl, moduleId) {
- if (!requestUrl || !moduleId) return null
- if (requestUrl.includes('api.php')) {
- return requestUrl.endsWith('=') || requestUrl.endsWith('/')
- ? requestUrl + moduleId
- : requestUrl + moduleId
- }
- return '/api.php?r=' + requestUrl + moduleId
- }
- function normalizeItems(payload) {
- if (!payload) return []
- if (Array.isArray(payload)) return payload
- if (Array.isArray(payload.items)) return payload.items
- if (Array.isArray(payload.data)) return payload.data
- if (Array.isArray(payload?.data?.items)) return payload.data.items
- return []
- }
- function itemPrice(item) {
- const raw =
- item?.price ??
- item?.price_single ??
- item?.price_f ??
- item?.[0] ??
- 0
- const n = Number(raw)
- return Number.isFinite(n) ? n : 0
- }
- function itemTitle(item) {
- return String(item?.title || item?.name || item?.id || '')
- }
- export default function PaymentItems(props) {
- const { t } = useTranslation()
- const formContext = useFormContext()
- const moduleId = useWatch({ control: formContext.control, name: 'module_id' })
- const sellerId = useWatch({ control: formContext.control, name: 'seller_id' })
- const [filter, setFilter] = useState('')
- const [selected, setSelected] = useState({})
- const requestUrl =
- props.request_url ||
- props.module_request_url ||
- '/api.php?r=bx_payment/get_items¶ms[]=single¶ms[]='
- const id = Number(moduleId)
- let itemsUrl = id ? resolveItemsUrl(requestUrl, id) : null
- if (itemsUrl && sellerId) {
- itemsUrl += (itemsUrl.includes('?') ? '&' : '?') + 'seller_id=' + encodeURIComponent(sellerId)
- }
- // isInitialLoading: no spinner for a disabled query or a cached module.
- const { data: itemsResponse, isInitialLoading: loading } = useFetch(itemsUrl)
- const items = useMemo(
- () => (itemsUrl ? normalizeItems(itemsResponse?.data ?? itemsResponse) : []),
- [itemsUrl, itemsResponse]
- )
- // Module cleared → drop the selection too.
- useEffect(() => {
- if (id) return
- setSelected({})
- formContext.setValue('items', [])
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [id])
- useEffect(() => {
- const ids = Object.keys(selected).filter((id) => selected[id]?.checked)
- formContext.setValue('items', ids)
- ids.forEach((id) => {
- const row = selected[id]
- formContext.setValue(`item-price-${id}`, String(row.price ?? ''))
- formContext.setValue(`item-quantity-${id}`, String(row.quantity ?? 1))
- })
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [selected])
- const toggleItem = (item) => {
- const id = String(item.id)
- setSelected((prev) => {
- const next = { ...prev }
- if (next[id]?.checked) {
- next[id] = { ...next[id], checked: false }
- } else {
- next[id] = {
- checked: true,
- price: next[id]?.price ?? itemPrice(item),
- quantity: next[id]?.quantity ?? 1,
- }
- }
- return next
- })
- }
- const updateField = (id, field, value, item) => {
- setSelected((prev) => ({
- ...prev,
- [id]: {
- checked: true,
- price: prev[id]?.price ?? itemPrice(item),
- quantity: prev[id]?.quantity ?? 1,
- ...prev[id],
- [field]: value,
- },
- }))
- }
- if (!Number(moduleId)) {
- return null
- }
- const filterLower = filter.trim().toLowerCase()
- const visibleItems = filterLower
- ? items.filter((item) => itemTitle(item).toLowerCase().includes(filterLower))
- : items
- return (
- <Field {...props} error2={formContext.formState.errors?.items}>
- <View className="w-full gap-2 border border-border/60 rounded-xl p-3 bg-card">
- <Input
- placeholder={t('Search')}
- value={filter}
- onChangeText={setFilter}
- />
- <Row className="items-center gap-2">
- <Text className="flex-1 min-w-0 text-sm font-medium text-muted-foreground">
- {t('Product')}
- </Text>
- <Text className="w-20 shrink-0 text-sm font-medium text-muted-foreground text-right">
- {t('Price')}
- </Text>
- <Text className="w-24 shrink-0 text-sm font-medium text-muted-foreground text-center">
- {t('Sold Price')}
- </Text>
- <Text className="w-14 shrink-0 text-sm font-medium text-muted-foreground text-center">
- {t('Qt.')}
- </Text>
- </Row>
- {loading ? (
- <View className="items-center py-4">
- <Loading />
- </View>
- ) : visibleItems.length === 0 ? (
- <Text className="text-sm text-muted-foreground text-center py-3">
- {t('Nothing found')}
- </Text>
- ) : (
- <View className="gap-2">
- {visibleItems.map((item) => {
- const id = String(item.id)
- const checked = !!selected[id]?.checked
- const price = itemPrice(item)
- return (
- <Row
- key={id}
- className="items-center gap-2 py-1 border-t border-border/40"
- >
- <View className="flex-1 min-w-0">
- <CheckBox
- value={checked}
- status={checked ? 'checked' : 'unchecked'}
- onPress={() => toggleItem(item)}
- title={itemTitle(item)}
- />
- </View>
- <Text className="w-20 shrink-0 text-sm text-foreground text-right">
- {price.toFixed(2)}
- </Text>
- <View className="w-24 shrink-0">
- <Input
- value={String(
- selected[id]?.price ?? price
- )}
- onChangeText={(text) =>
- updateField(id, 'price', text, item)
- }
- keyboardType="decimal-pad"
- />
- </View>
- <View className="w-14 shrink-0">
- <Input
- value={String(
- selected[id]?.quantity ?? 1
- )}
- onChangeText={(text) =>
- updateField(id, 'quantity', text, item)
- }
- keyboardType="number-pad"
- />
- </View>
- </Row>
- )
- })}
- </View>
- )}
- </View>
- </Field>
- )
- }
|