mirror of
https://github.com/sakaljurgis/best-choice.git
synced 2026-07-08 21:47:40 +00:00
price edit below the item
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useMemo, useRef, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import type { Item } from '@shared/models/item';
|
import type { Item } from '@shared/models/item';
|
||||||
import {
|
import {
|
||||||
@@ -19,7 +19,6 @@ import { ProjectHeader } from './project-header';
|
|||||||
import { ProjectDescriptionSection } from './project-description-section';
|
import { ProjectDescriptionSection } from './project-description-section';
|
||||||
import { TrackedAttributesSection } from './tracked-attributes-section';
|
import { TrackedAttributesSection } from './tracked-attributes-section';
|
||||||
import { ProjectItemsSection } from './project-items-section';
|
import { ProjectItemsSection } from './project-items-section';
|
||||||
import { ItemPricesModal } from './item-prices-modal';
|
|
||||||
|
|
||||||
export function ProjectDetailPage() {
|
export function ProjectDetailPage() {
|
||||||
const { projectId } = useParams<{ projectId: string }>();
|
const { projectId } = useParams<{ projectId: string }>();
|
||||||
@@ -29,7 +28,6 @@ export function ProjectDetailPage() {
|
|||||||
const updateItemMutation = useUpdateItemMutation(projectId);
|
const updateItemMutation = useUpdateItemMutation(projectId);
|
||||||
const updateProjectMutation = useUpdateProjectMutation(projectId);
|
const updateProjectMutation = useUpdateProjectMutation(projectId);
|
||||||
|
|
||||||
const [priceModalItemId, setPriceModalItemId] = useState<string | null>(null);
|
|
||||||
const [quickUrl, setQuickUrl] = useState('');
|
const [quickUrl, setQuickUrl] = useState('');
|
||||||
const [quickError, setQuickError] = useState<string | null>(null);
|
const [quickError, setQuickError] = useState<string | null>(null);
|
||||||
const [isItemModalOpen, setIsItemModalOpen] = useState(false);
|
const [isItemModalOpen, setIsItemModalOpen] = useState(false);
|
||||||
@@ -44,17 +42,6 @@ export function ProjectDetailPage() {
|
|||||||
const project = projectQuery.data;
|
const project = projectQuery.data;
|
||||||
const items = useMemo(() => itemsQuery.data?.data ?? [], [itemsQuery.data]);
|
const items = useMemo(() => itemsQuery.data?.data ?? [], [itemsQuery.data]);
|
||||||
const projectAttributes = useMemo(() => project?.attributes ?? [], [project]);
|
const projectAttributes = useMemo(() => project?.attributes ?? [], [project]);
|
||||||
const priceModalItem = useMemo(
|
|
||||||
() => items.find((item) => item.id === priceModalItemId) ?? null,
|
|
||||||
[items, priceModalItemId]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (priceModalItemId && !priceModalItem) {
|
|
||||||
setPriceModalItemId(null);
|
|
||||||
}
|
|
||||||
}, [priceModalItemId, priceModalItem]);
|
|
||||||
|
|
||||||
const availableAttributes = useMemo(() => {
|
const availableAttributes = useMemo(() => {
|
||||||
const attributeSet = new Set<string>();
|
const attributeSet = new Set<string>();
|
||||||
|
|
||||||
@@ -229,14 +216,6 @@ export function ProjectDetailPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditPrices = (item: Item) => {
|
|
||||||
setPriceModalItemId(item.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePricesModalClose = () => {
|
|
||||||
setPriceModalItemId(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const modalInitialData =
|
const modalInitialData =
|
||||||
itemModalMode === 'url'
|
itemModalMode === 'url'
|
||||||
? importedItemData
|
? importedItemData
|
||||||
@@ -295,7 +274,7 @@ export function ProjectDetailPage() {
|
|||||||
onAddManual={handleAddManualClick}
|
onAddManual={handleAddManualClick}
|
||||||
isImportingFromUrl={isImportingItem}
|
isImportingFromUrl={isImportingItem}
|
||||||
onEditItem={handleEditItem}
|
onEditItem={handleEditItem}
|
||||||
onEditPrices={handleEditPrices}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ItemFormModal
|
<ItemFormModal
|
||||||
@@ -311,12 +290,6 @@ export function ProjectDetailPage() {
|
|||||||
isInitialDataLoading={itemModalInitialDataLoading}
|
isInitialDataLoading={itemModalInitialDataLoading}
|
||||||
initialDataError={itemModalInitialDataError}
|
initialDataError={itemModalInitialDataError}
|
||||||
/>
|
/>
|
||||||
<ItemPricesModal
|
|
||||||
isOpen={Boolean(priceModalItem)}
|
|
||||||
item={priceModalItem}
|
|
||||||
projectId={projectId}
|
|
||||||
onClose={handlePricesModalClose}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { Fragment, useEffect, useMemo, useState } from 'react';
|
||||||
import { ExternalLink, Pencil } from 'lucide-react';
|
import { ExternalLink, Pencil } from 'lucide-react';
|
||||||
import type { Item } from '@shared/models/item';
|
import type { Item } from '@shared/models/item';
|
||||||
|
import { ItemPricesPanel } from './item-prices-panel';
|
||||||
|
|
||||||
interface ProjectItemsSectionProps {
|
interface ProjectItemsSectionProps {
|
||||||
items: Item[];
|
items: Item[];
|
||||||
@@ -14,7 +15,7 @@ interface ProjectItemsSectionProps {
|
|||||||
onAddManual: () => void;
|
onAddManual: () => void;
|
||||||
isImportingFromUrl: boolean;
|
isImportingFromUrl: boolean;
|
||||||
onEditItem: (item: Item) => void;
|
onEditItem: (item: Item) => void;
|
||||||
onEditPrices: (item: Item) => void;
|
projectId: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatAttributeValue = (value: unknown): string => {
|
const formatAttributeValue = (value: unknown): string => {
|
||||||
@@ -144,105 +145,66 @@ const getPriceSummaryDisplay = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resolveCurrency = (preferred: string | null) => preferred ?? summary.currency ?? null;
|
const resolveCurrency = (preferred: string | null) => preferred ?? summary.currency ?? null;
|
||||||
const formatConditionPrice = (
|
const formatConditionLabel = (
|
||||||
amount: number | null,
|
|
||||||
condition: 'new' | 'used',
|
condition: 'new' | 'used',
|
||||||
useFrom: boolean,
|
amount: number | null,
|
||||||
|
count: number,
|
||||||
currency: string | null,
|
currency: string | null,
|
||||||
isMixed: boolean
|
hasMixedCurrency: boolean
|
||||||
): string | null => {
|
): string | null => {
|
||||||
if (amount === null || isMixed) {
|
if (!count) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (amount === null || hasMixedCurrency) {
|
||||||
|
return `${condition} prices vary (${count})`;
|
||||||
|
}
|
||||||
const resolvedCurrency = resolveCurrency(currency);
|
const resolvedCurrency = resolveCurrency(currency);
|
||||||
const formatted = formatPriceAmount(amount, resolvedCurrency);
|
const formatted = formatPriceAmount(amount, resolvedCurrency);
|
||||||
const prefix = useFrom ? 'from ' : '';
|
return `${condition} from ${formatted} (${count})`;
|
||||||
return `${prefix}${formatted} ${condition}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasMultiplePrices = summary.priceCount > 1;
|
const newLabel = formatConditionLabel(
|
||||||
const hasNewPrices =
|
|
||||||
summary.newCount > 0 && summary.newMinAmount !== null && !summary.newHasMixedCurrency;
|
|
||||||
const hasUsedPrices =
|
|
||||||
summary.usedCount > 0 && summary.usedMinAmount !== null && !summary.usedHasMixedCurrency;
|
|
||||||
|
|
||||||
if (summary.priceCount === 1) {
|
|
||||||
if (hasNewPrices) {
|
|
||||||
const label = formatConditionPrice(
|
|
||||||
summary.newMinAmount,
|
|
||||||
'new',
|
'new',
|
||||||
false,
|
summary.newMinAmount,
|
||||||
|
summary.newCount,
|
||||||
summary.newCurrency,
|
summary.newCurrency,
|
||||||
summary.newHasMixedCurrency
|
summary.newHasMixedCurrency
|
||||||
);
|
);
|
||||||
if (label) {
|
const usedLabel = formatConditionLabel(
|
||||||
return { primary: label, secondary: null };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasUsedPrices) {
|
|
||||||
const label = formatConditionPrice(
|
|
||||||
summary.usedMinAmount,
|
|
||||||
'used',
|
'used',
|
||||||
false,
|
summary.usedMinAmount,
|
||||||
|
summary.usedCount,
|
||||||
summary.usedCurrency,
|
summary.usedCurrency,
|
||||||
summary.usedHasMixedCurrency
|
summary.usedHasMixedCurrency
|
||||||
);
|
);
|
||||||
if (label) {
|
|
||||||
return { primary: label, secondary: null };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { primary: formatPriceAmount(summary.minAmount, summary.currency), secondary: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasMultiplePrices) {
|
if (newLabel || usedLabel) {
|
||||||
const parts: string[] = [];
|
if (newLabel && usedLabel) {
|
||||||
|
return { primary: newLabel, secondary: usedLabel };
|
||||||
if (hasNewPrices) {
|
|
||||||
const label = formatConditionPrice(
|
|
||||||
summary.newMinAmount,
|
|
||||||
'new',
|
|
||||||
true,
|
|
||||||
summary.newCurrency,
|
|
||||||
summary.newHasMixedCurrency
|
|
||||||
);
|
|
||||||
if (label) {
|
|
||||||
parts.push(label);
|
|
||||||
}
|
}
|
||||||
}
|
return { primary: newLabel ?? usedLabel ?? '—', secondary: null };
|
||||||
|
|
||||||
if (hasUsedPrices) {
|
|
||||||
const label = formatConditionPrice(
|
|
||||||
summary.usedMinAmount,
|
|
||||||
'used',
|
|
||||||
true,
|
|
||||||
summary.usedCurrency,
|
|
||||||
summary.usedHasMixedCurrency
|
|
||||||
);
|
|
||||||
if (label) {
|
|
||||||
parts.push(label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parts.length === 1) {
|
|
||||||
return { primary: parts[0], secondary: null };
|
|
||||||
}
|
|
||||||
if (parts.length >= 2) {
|
|
||||||
return { primary: parts[0], secondary: parts[1] };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!summary.hasMixedCurrency) {
|
if (!summary.hasMixedCurrency) {
|
||||||
const formattedMin = formatPriceAmount(summary.minAmount, summary.currency);
|
const resolvedCurrency = resolveCurrency(null);
|
||||||
const formattedMax = formatPriceAmount(summary.maxAmount, summary.currency);
|
const formattedMin = formatPriceAmount(summary.minAmount, resolvedCurrency);
|
||||||
|
const formattedMax = formatPriceAmount(summary.maxAmount, resolvedCurrency);
|
||||||
if (summary.minAmount === summary.maxAmount) {
|
if (summary.minAmount === summary.maxAmount) {
|
||||||
return { primary: formattedMin, secondary: null };
|
return {
|
||||||
|
primary: `${formattedMin} (${summary.priceCount})`,
|
||||||
|
secondary: null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return { primary: `from ${formattedMin}`, secondary: `up to ${formattedMax}` };
|
return {
|
||||||
|
primary: `from ${formattedMin} (${summary.priceCount})`,
|
||||||
|
secondary: `up to ${formattedMax}`
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { primary: 'Multiple currencies', secondary: null };
|
return {
|
||||||
}
|
primary: `Multiple currencies (${summary.priceCount})`,
|
||||||
|
secondary: null
|
||||||
return { primary: formatPriceAmount(summary.minAmount, summary.currency), secondary: null };
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ProjectItemsSection({
|
export function ProjectItemsSection({
|
||||||
@@ -257,9 +219,10 @@ export function ProjectItemsSection({
|
|||||||
onAddManual,
|
onAddManual,
|
||||||
isImportingFromUrl,
|
isImportingFromUrl,
|
||||||
onEditItem,
|
onEditItem,
|
||||||
onEditPrices
|
projectId
|
||||||
}: ProjectItemsSectionProps) {
|
}: ProjectItemsSectionProps) {
|
||||||
const [showDifferencesOnly, setShowDifferencesOnly] = useState(false);
|
const [showDifferencesOnly, setShowDifferencesOnly] = useState(false);
|
||||||
|
const [expandedItemId, setExpandedItemId] = useState<string | null>(null);
|
||||||
|
|
||||||
const mismatchedAttributes = useMemo(() => {
|
const mismatchedAttributes = useMemo(() => {
|
||||||
const result = new Set<string>();
|
const result = new Set<string>();
|
||||||
@@ -303,6 +266,16 @@ export function ProjectItemsSection({
|
|||||||
}
|
}
|
||||||
}, [showDifferencesOnly, mismatchedAttributes]);
|
}, [showDifferencesOnly, mismatchedAttributes]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!expandedItemId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const itemStillPresent = items.some((item) => item.id === expandedItemId);
|
||||||
|
if (!itemStillPresent) {
|
||||||
|
setExpandedItemId(null);
|
||||||
|
}
|
||||||
|
}, [items, expandedItemId]);
|
||||||
|
|
||||||
const visibleAttributes = useMemo(() => {
|
const visibleAttributes = useMemo(() => {
|
||||||
if (!showDifferencesOnly) {
|
if (!showDifferencesOnly) {
|
||||||
return projectAttributes;
|
return projectAttributes;
|
||||||
@@ -315,6 +288,10 @@ export function ProjectItemsSection({
|
|||||||
const showNoDifferencesMessage =
|
const showNoDifferencesMessage =
|
||||||
!hasAttributeDifferences && projectAttributes.length > 0 && items.length > 1;
|
!hasAttributeDifferences && projectAttributes.length > 0 && items.length > 1;
|
||||||
|
|
||||||
|
const handleTogglePrices = (itemId: string) => {
|
||||||
|
setExpandedItemId((current) => (current === itemId ? null : itemId));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
|
<section className="rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
|
||||||
@@ -376,9 +353,12 @@ export function ProjectItemsSection({
|
|||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
const attributes = (item.attributes ?? {}) as Record<string, unknown>;
|
const attributes = (item.attributes ?? {}) as Record<string, unknown>;
|
||||||
const priceDisplay = getPriceSummaryDisplay(item.priceSummary);
|
const priceDisplay = getPriceSummaryDisplay(item.priceSummary);
|
||||||
|
const isExpanded = expandedItemId === item.id;
|
||||||
|
const desktopPanelId = `item-${item.id}-prices-desktop`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={item.id} className="hover:bg-slate-50">
|
<Fragment key={item.id}>
|
||||||
|
<tr className={`hover:bg-slate-50 ${isExpanded ? 'bg-slate-50' : ''}`}>
|
||||||
<td className="px-4 py-3 align-top">
|
<td className="px-4 py-3 align-top">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -403,7 +383,7 @@ export function ProjectItemsSection({
|
|||||||
href={item.sourceUrl}
|
href={item.sourceUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-blue-200 text-blue-600 transition hover:bg-blue-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
className="inline-flex h-8 w-8 items-center justify-center rounded-md text-blue-600 transition focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
||||||
title="Open source link"
|
title="Open source link"
|
||||||
>
|
>
|
||||||
<ExternalLink aria-hidden className="h-4 w-4" />
|
<ExternalLink aria-hidden className="h-4 w-4" />
|
||||||
@@ -422,28 +402,40 @@ export function ProjectItemsSection({
|
|||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
<td className="px-4 py-3 align-top">
|
<td className="px-4 py-3 align-top">
|
||||||
<div className="flex items-start justify-between gap-4">
|
|
||||||
<div className="flex flex-col gap-0.5 text-xs">
|
<div className="flex flex-col gap-0.5 text-xs">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm font-semibold text-slate-900">
|
<span className="text-sm font-semibold text-slate-900">
|
||||||
{priceDisplay.primary}
|
{priceDisplay.primary}
|
||||||
</span>
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleTogglePrices(item.id)}
|
||||||
|
className="inline-flex h-8 w-8 items-center justify-center rounded-md text-slate-500 transition hover:text-slate-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
||||||
|
aria-label="Edit Prices"
|
||||||
|
title="Edit Prices"
|
||||||
|
aria-expanded={isExpanded}
|
||||||
|
aria-controls={desktopPanelId}
|
||||||
|
>
|
||||||
|
<Pencil aria-hidden className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Edit Prices</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{priceDisplay.secondary ? (
|
{priceDisplay.secondary ? (
|
||||||
<span className="text-slate-500">{priceDisplay.secondary}</span>
|
<span className="text-slate-500">{priceDisplay.secondary}</span>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<button
|
</td>
|
||||||
type="button"
|
</tr>
|
||||||
onClick={() => onEditPrices(item)}
|
{isExpanded ? (
|
||||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-blue-200 text-blue-600 transition hover:bg-blue-50"
|
<tr className="bg-slate-50">
|
||||||
aria-label="Edit Prices"
|
<td colSpan={visibleAttributes.length + 2} className="px-4 py-4">
|
||||||
title="Edit Prices"
|
<div id={desktopPanelId}>
|
||||||
>
|
<ItemPricesPanel itemId={item.id} projectId={projectId} />
|
||||||
<Pencil aria-hidden className="h-5 w-5 text-slate-400 transition hover:text-blue-600" />
|
|
||||||
<span className="sr-only">Edit Prices</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
) : null}
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -455,6 +447,8 @@ export function ProjectItemsSection({
|
|||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
const attributes = (item.attributes ?? {}) as Record<string, unknown>;
|
const attributes = (item.attributes ?? {}) as Record<string, unknown>;
|
||||||
const priceDisplay = getPriceSummaryDisplay(item.priceSummary);
|
const priceDisplay = getPriceSummaryDisplay(item.priceSummary);
|
||||||
|
const isExpanded = expandedItemId === item.id;
|
||||||
|
const mobilePanelId = `item-${item.id}-prices-mobile`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={item.id} className="rounded-xl border border-slate-200 p-4 shadow-sm">
|
<div key={item.id} className="rounded-xl border border-slate-200 p-4 shadow-sm">
|
||||||
@@ -480,7 +474,7 @@ export function ProjectItemsSection({
|
|||||||
href={item.sourceUrl}
|
href={item.sourceUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="inline-flex h-9 w-9 items-center justify-center rounded-md border border-blue-200 text-blue-600 transition hover:bg-blue-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
className="inline-flex h-9 w-9 items-center justify-center rounded-md text-blue-600 transition focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
||||||
title="Open source link"
|
title="Open source link"
|
||||||
>
|
>
|
||||||
<ExternalLink aria-hidden className="h-5 w-5" />
|
<ExternalLink aria-hidden className="h-5 w-5" />
|
||||||
@@ -506,19 +500,32 @@ export function ProjectItemsSection({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 flex flex-col gap-0.5 text-xs">
|
<div className="mt-3 flex flex-col gap-0.5 text-xs">
|
||||||
<span className="text-sm font-semibold text-slate-900">{priceDisplay.primary}</span>
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-sm font-semibold text-slate-900">
|
||||||
|
{priceDisplay.primary}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleTogglePrices(item.id)}
|
||||||
|
className="inline-flex h-9 w-9 items-center justify-center rounded-md text-slate-500 transition hover:text-slate-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
|
||||||
|
aria-label="Edit Prices"
|
||||||
|
title="Edit Prices"
|
||||||
|
aria-expanded={isExpanded}
|
||||||
|
aria-controls={mobilePanelId}
|
||||||
|
>
|
||||||
|
<Pencil aria-hidden className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Edit Prices</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{priceDisplay.secondary ? (
|
{priceDisplay.secondary ? (
|
||||||
<span className="text-slate-500">{priceDisplay.secondary}</span>
|
<span className="text-slate-500">{priceDisplay.secondary}</span>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<button
|
{isExpanded ? (
|
||||||
type="button"
|
<div id={mobilePanelId} className="mt-4">
|
||||||
onClick={() => onEditPrices(item)}
|
<ItemPricesPanel itemId={item.id} projectId={projectId} />
|
||||||
className="mt-4 inline-flex w-full items-center justify-center gap-2 rounded-md border border-blue-200 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-blue-600 transition hover:bg-blue-50"
|
</div>
|
||||||
>
|
) : null}
|
||||||
<Pencil aria-hidden className="h-5 w-5 text-slate-400 transition hover:text-blue-600" />
|
|
||||||
Edit Prices
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user