mirror of
https://github.com/vercel/commerce.git
synced 2025-05-13 05:07:51 +00:00
create new product mapper page
This commit is contained in:
parent
8f842f0efe
commit
6c016248fe
25
@/components/ui/input.tsx
Normal file
25
@/components/ui/input.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export interface InputProps
|
||||||
|
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||||
|
|
||||||
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
|
({ className, type, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className={cn(
|
||||||
|
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Input.displayName = "Input"
|
||||||
|
|
||||||
|
export { Input }
|
101
app/sos/page.tsx
Normal file
101
app/sos/page.tsx
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// make a page where you can select from garment types, collection types and numbers to create a copyable name
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Combox } from "components/combox";
|
||||||
|
import { ProductSKUs } from "components/product/sku-generator";
|
||||||
|
import { collectionsSKUs, garmentHandleKeys } from "constants/sku";
|
||||||
|
import { capitalizeFirstLetter, copyText, getKeyByValue } from "lib/helpers/actions";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
// type SKUSelectorState = {
|
||||||
|
// garment?: keyof typeof garmentHandleKeys,
|
||||||
|
// collection?: keyof typeof collectionsSKUs,
|
||||||
|
// };
|
||||||
|
|
||||||
|
export default async function NewProductHelpPage() {
|
||||||
|
// const [open, setOpen] = useState(false)
|
||||||
|
const [collection, setCollection] = useState<keyof typeof collectionsSKUs | null>(null)
|
||||||
|
const [garment, setGarment] = useState<keyof typeof garmentHandleKeys | null>(null)
|
||||||
|
const [title, setTitle] = useState<string | null>(null)
|
||||||
|
const [number, setNumber] = useState<string | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const title = createTitle()
|
||||||
|
console.log("🍓🍋🍊 title", title);
|
||||||
|
|
||||||
|
}, [collection, garment])
|
||||||
|
|
||||||
|
const garmentValues = Object.keys(garmentHandleKeys)
|
||||||
|
const collectionValues = Object.keys(collectionsSKUs)
|
||||||
|
|
||||||
|
const optionsMapper = (keys: {}, values: string[]) => {
|
||||||
|
return values.map(value => {
|
||||||
|
return {
|
||||||
|
key: keys[value as keyof typeof keys],
|
||||||
|
label: value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const garmentOptions = optionsMapper(garmentHandleKeys, garmentValues);
|
||||||
|
const collectionOptions = optionsMapper(collectionsSKUs, collectionValues);
|
||||||
|
|
||||||
|
const createTitle = () => {
|
||||||
|
let collectionMapper = getKeyByValue(collectionsSKUs, collection)
|
||||||
|
if (collectionMapper) collectionMapper = capitalizeFirstLetter(collectionMapper)
|
||||||
|
let garmentMapper = getKeyByValue(garmentHandleKeys, garment)
|
||||||
|
if (garmentMapper) garmentMapper = capitalizeFirstLetter(garmentMapper)
|
||||||
|
setTitle(`${collectionMapper}scape No.${number} ${garmentMapper}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="m-20">
|
||||||
|
<div className="grid grid-cols-5">
|
||||||
|
<div className="py-6 col-span-4 col-start-2">
|
||||||
|
<p className="title">New Product Creator</p>
|
||||||
|
</div>
|
||||||
|
<div className="py-6 col-span-1 col-start-2">
|
||||||
|
<Combox
|
||||||
|
options={collectionOptions}
|
||||||
|
onShow={(key: string): void => setCollection(key as keyof typeof collectionsSKUs)}
|
||||||
|
currentKey={collection || null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="pt-6 mx-30 col-span-1 col-start-3 flex justify-center items-start">
|
||||||
|
<Input
|
||||||
|
className="w-2/3"
|
||||||
|
type="number"
|
||||||
|
placeholder="No."
|
||||||
|
onChange={(e) => setNumber(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="pt-6 col-span-1 col-start-4">
|
||||||
|
<Combox
|
||||||
|
options={garmentOptions}
|
||||||
|
onShow={(key: string): void => setGarment(key as keyof typeof garmentHandleKeys)}
|
||||||
|
currentKey={garment || null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{garment && collection && number && title &&
|
||||||
|
<>
|
||||||
|
<div className="pb-6 col-span-3 col-start-2">
|
||||||
|
<div
|
||||||
|
className="border p-2 px-4 cursor-pointer"
|
||||||
|
onClick={() => copyText(title)}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="my-5 col-span-5">
|
||||||
|
<ProductSKUs productTitle={title} noTitle={true} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -23,9 +23,17 @@ type Option = {
|
|||||||
label: string,
|
label: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Combox({options}: {options: Option[]}) {
|
export function Combox(
|
||||||
|
{
|
||||||
|
options,
|
||||||
|
currentKey,
|
||||||
|
onShow,
|
||||||
|
}: {
|
||||||
|
options: Option[],
|
||||||
|
currentKey: string | null,
|
||||||
|
onShow: ((key: string) => void),
|
||||||
|
}) {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [currentKey, setCurrentKey] = useState<string | null>(null)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
@ -34,15 +42,15 @@ export function Combox({options}: {options: Option[]}) {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
role="combobox"
|
role="combobox"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
className="w-[200px] justify-between"
|
className="w-[200px] justify-between truncate text-ellipsis"
|
||||||
>
|
>
|
||||||
{currentKey
|
{currentKey
|
||||||
? options.find((option) => option.key === currentKey)?.label
|
? options.find((option) => option.key === currentKey)?.label
|
||||||
: "Select option..."}
|
: "Select option..."}
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger> <br /><br />
|
||||||
<PopoverContent className="w-[200px] p-0">
|
<PopoverContent className="w-[200px] p-0 bg-popover">
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder="Search option..." />
|
<CommandInput placeholder="Search option..." />
|
||||||
<CommandEmpty>No option found.</CommandEmpty>
|
<CommandEmpty>No option found.</CommandEmpty>
|
||||||
@ -51,8 +59,9 @@ export function Combox({options}: {options: Option[]}) {
|
|||||||
<CommandItem
|
<CommandItem
|
||||||
key={option.key}
|
key={option.key}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
setCurrentKey(option.key)
|
// setCurrentKey(option.key)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
|
onShow(option.key)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Check
|
<Check
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import { copyText } from "lib/helpers/actions";
|
||||||
import { createProductSKUs } from "lib/helpers/skus";
|
import { createProductSKUs } from "lib/helpers/skus";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
|
||||||
export function ProductSKUs(productInfo: {productTitle: string}) {
|
export function ProductSKUs(
|
||||||
const SKUs = createProductSKUs(productInfo.productTitle)
|
{
|
||||||
|
productTitle,
|
||||||
|
noTitle = true
|
||||||
|
}: { productTitle: string, noTitle: boolean}) {
|
||||||
|
const SKUs = createProductSKUs(productTitle)
|
||||||
const [copyMessageState, setCopyMessageState] = useState< number | null >(null);
|
const [copyMessageState, setCopyMessageState] = useState< number | null >(null);
|
||||||
|
|
||||||
const triggerCopyMessage = async (e: MouseEvent<HTMLButtonElement>, i: number) => {
|
const triggerCopyMessage = async (i: number, SKU: string) => {
|
||||||
copyText(e.target.value)
|
copyText(SKU)
|
||||||
|
|
||||||
console.log("copyMessageState", copyMessageState);
|
console.log("copyMessageState", copyMessageState);
|
||||||
setCopyMessageState(i);
|
setCopyMessageState(i);
|
||||||
@ -19,22 +24,18 @@ export function ProductSKUs(productInfo: {productTitle: string}) {
|
|||||||
setTimeout(() => setCopyMessageState(null), 2500)
|
setTimeout(() => setCopyMessageState(null), 2500)
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyText(text: string) {
|
|
||||||
navigator.clipboard.writeText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mx-auto max-w-screen-2xl px-4">
|
<div className="mx-auto max-w-screen-2xl px-4">
|
||||||
<div className="rounded-lg border border-neutral-200 bg-white p-8 px-4 dark:border-neutral-800 dark:bg-black md:p-12 lg:grid lg:grid-cols-12">
|
<div className="rounded-lg border border-neutral-200 bg-white p-8 px-4 dark:border-neutral-800 dark:bg-black md:p-12 lg:grid lg:grid-cols-12">
|
||||||
<div className="lg:col-span-6 lg:col-start-4">
|
<div className="lg:col-span-6 lg:col-start-4">
|
||||||
<h3 className="font-bold text-xl pb-4">{productInfo.productTitle}</h3>
|
{ !noTitle && <h3 className="font-bold text-xl pb-4">{productTitle}</h3> }
|
||||||
{/* {copyMessageState} */}
|
{/* {copyMessageState} */}
|
||||||
{SKUs?.map((SKU, index) => {
|
{SKUs?.map((SKU, index) => {
|
||||||
return (
|
return (
|
||||||
<div className="my-4 w-full" key={`${SKU}-${index}`}>
|
<div className="my-4 w-full" key={`${SKU}-${index}`}>
|
||||||
<div
|
<div
|
||||||
onClick={(e) => triggerCopyMessage(e, index)}
|
onClick={() => triggerCopyMessage(index, SKU)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
[
|
[
|
||||||
"cursor-pointer w-full border py-2 px-2 rounded",
|
"cursor-pointer w-full border py-2 px-2 rounded",
|
||||||
|
11
lib/helpers/actions.tsx
Normal file
11
lib/helpers/actions.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export function copyText(text: string) {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function capitalizeFirstLetter(string) {
|
||||||
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getKeyByValue(object, value) {
|
||||||
|
return Object.keys(object).find(key => object[key] === value);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user