Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/api/stores/[storeId]/products/filter/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ function transformProduct(product: any) {
const images = dataTransformer.transformImages(product.images, product.name);
const handle = dataTransformer.createHandle(product.name);
const attributes = dataTransformer.transformAttributes(product.attributes);
const tags = dataTransformer.transformTags(product.tags);

return {
id: product.id,
Expand All @@ -180,7 +181,7 @@ function transformProduct(product: any) {
status: product.status,
slug: handle,
featured: product.featured,
tags: product.tags,
tags: tags,
createdAt: product.createdAt,
updatedAt: product.updatedAt,
vendor: product.supplier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AIGenerateButton } from '@/app/store/components/product-management/prod
import { useProductDescription } from '@/app/store/components/product-management/products/hooks/useProductDescription';
import { useToast } from '@/app/store/context/ToastContext';
import type { ProductFormValues } from '@/lib/zod-schemas/product-schema';
import { Banner, BlockStack, Button, ButtonGroup, FormLayout, Text, TextField } from '@shopify/polaris';
import { Banner, BlockStack, Button, ButtonGroup, Card, FormLayout, Text, TextField } from '@shopify/polaris';
import { useState } from 'react';
import type { UseFormReturn } from 'react-hook-form';
import { Controller } from 'react-hook-form';
Expand Down Expand Up @@ -51,115 +51,117 @@ export function BasicInfoSection({ form }: BasicInfoSectionProps) {
};

return (
<BlockStack gap="400">
<Text as="h2" variant="headingMd">
Información Básica
</Text>
<FormLayout>
<Controller
name="name"
control={form.control}
render={({ field, fieldState }) => (
<TextField
{...field}
label="Nombre del Producto"
error={fieldState.error?.message}
autoComplete="off"
helpText="El nombre de su producto como aparecerá a los clientes."
/>
)}
/>
<BlockStack gap="200">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Text as="h3" variant="headingSm">
Descripción
</Text>
<AIGenerateButton
onClick={handleGenerateDescription}
isLoading={isGeneratingDescription}
isDisabled={!!previewDescription}
/>
</div>
{previewDescription && (
<Banner title="Vista previa de descripción generada" tone="info">
<BlockStack gap="200">
<Text as="p">{previewDescription}</Text>
<ButtonGroup>
<Button onClick={handleGenerateDescription} loading={isGeneratingDescription}>
Regenerar
</Button>
<Button onClick={rejectDescription}>Descartar</Button>
<Button onClick={acceptDescription} variant="primary">
Aplicar
</Button>
</ButtonGroup>
</BlockStack>
</Banner>
)}
<Card>
<BlockStack gap="400">
<Text as="h2" variant="headingMd">
Información Básica
</Text>
<FormLayout>
<Controller
name="description"
name="name"
control={form.control}
render={({ field, fieldState }) => (
<TextField
{...field}
label="Descripción"
labelHidden
multiline={4}
label="Nombre del Producto"
error={fieldState.error?.message}
autoComplete="off"
helpText="Proporcione una descripción detallada de su producto."
helpText="El nombre de su producto como aparecerá a los clientes."
/>
)}
/>
</BlockStack>

<FormLayout.Group>
<Controller
name="creationDate"
control={form.control}
render={({ field, fieldState }) => (
<TextField
label="Fecha de Creación"
type="date"
value={field.value ? new Date(field.value).toISOString().split('T')[0] : ''}
onChange={(dateString) => {
if (!dateString) {
field.onChange(null);
return;
}
const date = new Date(dateString);
const userTimezoneOffset = date.getTimezoneOffset() * 60000;
field.onChange(new Date(date.getTime() + userTimezoneOffset));
}}
onBlur={field.onBlur}
name={field.name}
error={fieldState.error?.message}
autoComplete="off"
helpText="Cuando se creó este producto."
<BlockStack gap="200">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Text as="h3" variant="headingSm">
Descripción
</Text>
<AIGenerateButton
onClick={handleGenerateDescription}
isLoading={isGeneratingDescription}
isDisabled={!!previewDescription}
/>
</div>
{previewDescription && (
<Banner title="Vista previa de descripción generada" tone="info">
<BlockStack gap="200">
<Text as="p">{previewDescription}</Text>
<ButtonGroup>
<Button onClick={handleGenerateDescription} loading={isGeneratingDescription}>
Regenerar
</Button>
<Button onClick={rejectDescription}>Descartar</Button>
<Button onClick={acceptDescription} variant="primary">
Aplicar
</Button>
</ButtonGroup>
</BlockStack>
</Banner>
)}
/>
<Controller
name="description"
control={form.control}
render={({ field, fieldState }) => (
<TextField
{...field}
label="Descripción"
labelHidden
multiline={4}
error={fieldState.error?.message}
autoComplete="off"
helpText="Proporcione una descripción detallada de su producto."
/>
)}
/>
</BlockStack>

<Controller
name="lastModifiedDate"
control={form.control}
render={({ field, fieldState }) => (
<TextField
label="Fecha de Última Modificación"
type="date"
value={field.value ? new Date(field.value).toISOString().split('T')[0] : ''}
onChange={field.onChange}
onBlur={field.onBlur}
name={field.name}
disabled
error={fieldState.error?.message}
autoComplete="off"
helpText="Se actualiza automáticamente cuando se guarda el producto."
/>
)}
/>
</FormLayout.Group>
</FormLayout>
</BlockStack>
<FormLayout.Group>
<Controller
name="creationDate"
control={form.control}
render={({ field, fieldState }) => (
<TextField
label="Fecha de Creación"
type="date"
value={field.value ? new Date(field.value).toISOString().split('T')[0] : ''}
onChange={(dateString) => {
if (!dateString) {
field.onChange(null);
return;
}
const date = new Date(dateString);
const userTimezoneOffset = date.getTimezoneOffset() * 60000;
field.onChange(new Date(date.getTime() + userTimezoneOffset));
}}
onBlur={field.onBlur}
name={field.name}
error={fieldState.error?.message}
autoComplete="off"
helpText="Cuando se creó este producto."
/>
)}
/>

<Controller
name="lastModifiedDate"
control={form.control}
render={({ field, fieldState }) => (
<TextField
label="Fecha de Última Modificación"
type="date"
value={field.value ? new Date(field.value).toISOString().split('T')[0] : ''}
onChange={field.onChange}
onBlur={field.onBlur}
name={field.name}
disabled
error={fieldState.error?.message}
autoComplete="off"
helpText="Se actualiza automáticamente cuando se guarda el producto."
/>
)}
/>
</FormLayout.Group>
</FormLayout>
</BlockStack>
</Card>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { ProductFormValues } from '@/lib/zod-schemas/product-schema';
import { BlockStack, Card, Grid, Text, TextField } from '@shopify/polaris';
import type { UseFormReturn } from 'react-hook-form';
import { Controller } from 'react-hook-form';

interface InventoryOnlySectionProps {
form: UseFormReturn<ProductFormValues>;
}

export function InventoryOnlySection({ form }: InventoryOnlySectionProps) {
return (
<Card>
<BlockStack gap="400">
<Text as="h2" variant="headingMd">
Inventario
</Text>
<Grid>
<Grid.Cell columnSpan={{ xs: 6, sm: 3, md: 3, lg: 6, xl: 6 }}>
<Controller
name="sku"
control={form.control}
render={({ field, fieldState }) => (
<TextField
{...field}
label="SKU (Código de Artículo)"
error={fieldState.error?.message}
autoComplete="off"
helpText="Un identificador único para su producto."
/>
)}
/>
</Grid.Cell>
<Grid.Cell columnSpan={{ xs: 6, sm: 3, md: 3, lg: 6, xl: 6 }}>
<Controller
name="barcode"
control={form.control}
render={({ field, fieldState }) => (
<TextField
{...field}
label="Código de Barras (ISBN, UPC, GTIN, etc.)"
error={fieldState.error?.message}
autoComplete="off"
helpText="Ingrese un código de barras para su producto."
/>
)}
/>
</Grid.Cell>
</Grid>
<Controller
name="quantity"
control={form.control}
render={({ field, fieldState }) => (
<TextField
{...field}
label="Cantidad"
type="number"
min={0}
step={1}
value={String(field.value ?? '')}
error={fieldState.error?.message}
autoComplete="off"
helpText="El número de artículos en stock."
/>
)}
/>
</BlockStack>
</Card>
);
}
Loading