Files
medassist-ng/frontend/src/components/MedDetailModal.tsx
T
Daniel Volz 4b697374f6 feat: obsolete medication archiving, start date, and UI improvements (#215)
* feat: obsolete medication archiving, start date, and UI improvements

- Add soft-archive (obsolete) for medications with dedicated section and toggle
- Add medication start date field with date picker and validation
- Add obsolete/reactivate API endpoints with proper auth
- Filter obsolete meds from schedule, coverage, planner, and notifications
- Improve UserFilterModal with intake schedules, stock badges, and click-to-open
- Improve dashboard taken-by badges with per-intake bell icons
- Add Escape key support to ConfirmModal and MobileEditModal
- Fix Lightbox close button positioning near image
- Add read-only mode support for MobileEditModal
- DB migrations: 0008 (is_obsolete, obsolete_at), 0009 (medication_start_date)
- All user-facing text uses i18n keys (en + de)

* test: fix tests for obsolete medications and UI changes

- Backend: add is_obsolete, obsolete_at, medication_start_date columns to test schemas
- Backend: add test medication inserts in planner tests for active-med filtering
- Frontend: update useMedications URL to include includeObsolete param
- Frontend: fix MobileEditModal selectors and validation assertions
- Frontend: add onClearUser prop to UserFilterModal test renders
- Frontend: fix MedicationsPage and DashboardPage test assertions
2026-02-15 23:23:38 +01:00

727 lines
26 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* MedDetailModal - Medication detail view with nested modals
* Displays medication information, stock, schedules, and provides refill/edit functionality
*
* Can work in two modes:
* 1. Context mode: Uses useAppContext() for all state (when no props provided)
* 2. Props mode: Accepts all required data as props (for gradual adoption)
*/
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Lightbox, MedicationAvatar } from "../components";
import type { Coverage, Medication, RefillEntry, StockThresholds } from "../types";
import { getMedTotal, getPackageSize } from "../types";
import { formatNumber, generateICS, getExpiryClass, getSystemLocale } from "../utils";
import { getStockStatus } from "../utils/schedule";
// =============================================================================
// Local Helper Functions
// =============================================================================
/**
* Calculate blister stock - divides current pills into full blisters and partial
*/
function getBlisterStock(
currentPills: number,
pillsPerBlister: number,
_originalLooseTablets: number,
_originalTotalPills: number
): { fullBlisters: number; openBlisterPills: number; loosePills: number } {
if (pillsPerBlister <= 0 || pillsPerBlister === 1) {
return { fullBlisters: 0, openBlisterPills: 0, loosePills: currentPills };
}
const fullBlisters = Math.floor(currentPills / pillsPerBlister);
const openBlisterPills = currentPills % pillsPerBlister;
return { fullBlisters, openBlisterPills, loosePills: 0 };
}
/**
* Format full blisters column
*/
function formatFullBlisters(fullBlisters: number, t: (key: string) => string): string {
if (fullBlisters === 0) return "—";
return `${fullBlisters} ${fullBlisters === 1 ? t("common.blister") : t("common.blisters")}`;
}
/**
* Format open blister column
*/
function formatOpenBlisterAndLoose(
openBlisterPills: number,
_loosePills: number,
pillsPerBlister: number,
t: (key: string) => string
): string {
if (openBlisterPills > 0) {
return `${openBlisterPills} ${t("common.of")} ${pillsPerBlister} ${t("common.pills")}`;
}
return "—";
}
// =============================================================================
// Props Interface
// =============================================================================
export interface MedDetailModalProps {
// Required
selectedMed: Medication | null;
coverage: { all: Coverage[] };
settings: StockThresholds;
// Modal state
showImageLightbox: boolean;
showRefillModal: boolean;
showEditStockModal: boolean;
// Modal actions
onClose: () => void;
onOpenImageLightbox: () => void;
onCloseImageLightbox: () => void;
onOpenRefillModal: () => void;
onCloseRefillModal: () => void;
onOpenEditStockModal: () => void;
onCloseEditStockModal: () => void;
// Refill state
refillPacks: number;
onRefillPacksChange: (value: number) => void;
refillLoose: number;
onRefillLooseChange: (value: number) => void;
usePrescriptionRefill: boolean;
onUsePrescriptionRefillChange: (value: boolean) => void;
refillSaving: boolean;
refillHistory: RefillEntry[];
refillHistoryExpanded: boolean;
onRefillHistoryExpandedChange: (value: boolean) => void;
onSubmitRefill: (medId: number, usePrescription?: boolean) => Promise<void>;
// Edit stock state
editStockFullBlisters: number;
onEditStockFullBlistersChange: (value: number) => void;
editStockPartialBlisterPills: number;
onEditStockPartialBlisterPillsChange: (value: number) => void;
editStockSaving: boolean;
onSubmitStockCorrection: (medId: number) => Promise<void>;
}
export function MedDetailModal({
selectedMed,
coverage,
settings,
showImageLightbox,
showRefillModal,
showEditStockModal,
onClose,
onOpenImageLightbox,
onCloseImageLightbox,
onOpenRefillModal,
onCloseRefillModal,
onOpenEditStockModal,
onCloseEditStockModal,
refillPacks,
onRefillPacksChange,
refillLoose,
onRefillLooseChange,
usePrescriptionRefill,
onUsePrescriptionRefillChange,
refillSaving,
refillHistory,
refillHistoryExpanded,
onRefillHistoryExpandedChange,
onSubmitRefill,
editStockFullBlisters,
onEditStockFullBlistersChange,
editStockPartialBlisterPills,
onEditStockPartialBlisterPillsChange,
editStockSaving,
onSubmitStockCorrection,
}: MedDetailModalProps) {
const { t, i18n } = useTranslation();
const [editStockFullInput, setEditStockFullInput] = useState("0");
const [editStockPartialInput, setEditStockPartialInput] = useState("0");
const parseStockInput = (value: string): number => {
const parsed = Number.parseInt(value, 10);
return Number.isNaN(parsed) ? 0 : parsed;
};
useEffect(() => {
if (showEditStockModal) {
setEditStockFullInput(String(editStockFullBlisters));
setEditStockPartialInput(String(editStockPartialBlisterPills));
}
}, [showEditStockModal, editStockFullBlisters, editStockPartialBlisterPills]);
if (!selectedMed) return null;
const medCoverage = coverage.all.find((c) => c.name === selectedMed.name);
const packageSize = getPackageSize(selectedMed);
const currentStock = medCoverage ? Math.round(medCoverage.medsLeft) : getMedTotal(selectedMed);
const status = medCoverage ? getStockStatus(medCoverage.daysLeft, medCoverage.medsLeft, settings) : null;
const textClass =
status?.className === "danger" ? "danger-text" : status?.className === "warning" ? "warning-text" : "success-text";
const stock = getBlisterStock(currentStock, selectedMed.pillsPerBlister, selectedMed.looseTablets, packageSize);
const fullForBounds = Math.max(0, parseStockInput(editStockFullInput));
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content med-detail-modal" onClick={(e) => e.stopPropagation()}>
<button className="modal-close" onClick={onClose}>
×
</button>
<div className="med-detail-body">
{/* Header */}
<div className="med-detail-header">
<div
className={`med-detail-avatar-wrapper ${selectedMed.imageUrl ? "clickable" : ""}`}
onClick={() => selectedMed.imageUrl && onOpenImageLightbox()}
>
<MedicationAvatar name={selectedMed.name} imageUrl={selectedMed.imageUrl} size="lg" />
{selectedMed.imageUrl && <span className="expand-icon">🔍</span>}
</div>
<div className="med-detail-titles">
<h2>{selectedMed.name}</h2>
{selectedMed.genericName && <span className="med-generic-name">{selectedMed.genericName}</span>}
{selectedMed.takenBy && (selectedMed.takenBy || []).length > 0 && (
<span className="med-taken-by">
{t("modal.for")}{" "}
{selectedMed.takenBy.map((person, index) => (
<span key={person}>
{index > 0 && ", "}
{person}
{selectedMed.intakes?.some(
(intake) => intake.takenBy === person && intake.intakeRemindersEnabled
) && <span className="taken-by-badge">🔔</span>}
</span>
))}
</span>
)}
</div>
</div>
{/* Stock Info Section */}
<div className="med-detail-section">
<h3>{t("modal.stockInfo")}</h3>
<div className="med-detail-grid">
{selectedMed.packageType === "blister" && (
<>
<div className="med-detail-item">
<span className="med-detail-label">{t("table.fullBlisters")}</span>
<span className={`med-detail-value ${textClass}`}>{formatFullBlisters(stock.fullBlisters, t)}</span>
</div>
<div className="med-detail-item">
<span className="med-detail-label">{t("table.openBlister")}</span>
<span className={`med-detail-value ${textClass}`}>
{formatOpenBlisterAndLoose(
stock.openBlisterPills,
stock.loosePills,
selectedMed.pillsPerBlister ?? 1,
t
)}
</span>
</div>
</>
)}
<div className={`med-detail-item ${selectedMed.packageType === "bottle" ? "full-width" : "full-width"}`}>
<span className="med-detail-label">{t("modal.currentStock")}</span>
<span className={`med-detail-value ${textClass}`}>
{currentStock} /{" "}
{selectedMed.packageType === "bottle" ? (selectedMed.totalPills ?? packageSize) : packageSize}
{currentStock >
(selectedMed.packageType === "bottle" ? (selectedMed.totalPills ?? packageSize) : packageSize) && (
<span
className="info-tooltip tooltip-align-left warning-text"
data-tooltip={t("tooltips.stockExceedsCapacity")}
>
{" "}
</span>
)}
</span>
</div>
</div>
</div>
{/* Package Details Section */}
<div className="med-detail-section">
<h3>
{t("modal.packageDetails")} (
{selectedMed.packageType === "bottle" ? t("form.packageTypeBottle") : t("form.packageTypeBlister")})
</h3>
<div className="med-detail-grid">
{selectedMed.packageType === "blister" ? (
<>
<div className="med-detail-item">
<span className="med-detail-label">{t("modal.packs")}</span>
<span className="med-detail-value">{selectedMed.packCount}</span>
</div>
<div className="med-detail-item">
<span className="med-detail-label">{t("modal.blistersPerPack")}</span>
<span className="med-detail-value">{selectedMed.blistersPerPack}</span>
</div>
<div className="med-detail-item">
<span className="med-detail-label">{t("modal.pillsPerBlister")}</span>
<span className="med-detail-value">{selectedMed.pillsPerBlister}</span>
</div>
</>
) : (
<div className="med-detail-item">
<span className="med-detail-label">{t("form.totalCapacity")}</span>
<span className="med-detail-value">{(selectedMed.totalPills ?? packageSize) || "—"}</span>
</div>
)}
{selectedMed.pillWeightMg && (
<div className="med-detail-item">
<span className="med-detail-label">{t("modal.pillWeight")}</span>
<span className="med-detail-value">
{selectedMed.pillWeightMg} {selectedMed.doseUnit ?? "mg"}
</span>
</div>
)}
{selectedMed.expiryDate && (
<div className="med-detail-item">
<span className="med-detail-label">{t("modal.expiryDate")}</span>
<span
className={`med-detail-value ${getExpiryClass(selectedMed.expiryDate, settings.expiryWarningDays)}`}
>
{new Date(selectedMed.expiryDate).toLocaleDateString(getSystemLocale(i18n.language), {
day: "2-digit",
month: "short",
year: "numeric",
})}
</span>
</div>
)}
</div>
</div>
{/* Prescription Details Section */}
{selectedMed.prescriptionEnabled && (
<div className="med-detail-section">
<h3>{t("form.sections.prescription")}</h3>
<div className="med-detail-grid prescription-detail-grid">
<div className="med-detail-item">
<span className="med-detail-label">{t("prescription.authorizedRefills")}</span>
<span className="med-detail-value">{selectedMed.prescriptionAuthorizedRefills ?? "—"}</span>
</div>
<div className="med-detail-item">
<span className="med-detail-label">{t("prescription.remainingRefills")}</span>
<span className="med-detail-value">{selectedMed.prescriptionRemainingRefills ?? "—"}</span>
</div>
<div className="med-detail-item">
<span className="med-detail-label">{t("prescription.lowThreshold")}</span>
<span className="med-detail-value">{selectedMed.prescriptionLowRefillThreshold ?? "—"}</span>
</div>
<div className="med-detail-item">
<span className="med-detail-label">{t("prescription.expiryDate")}</span>
<span className="med-detail-value">
{selectedMed.prescriptionExpiryDate
? new Date(selectedMed.prescriptionExpiryDate).toLocaleDateString(
getSystemLocale(i18n.language),
{
day: "2-digit",
month: "short",
year: "numeric",
}
)
: "—"}
</span>
</div>
</div>
</div>
)}
{/* Intake Schedule Section */}
{selectedMed.blisters.length > 0 && (
<div className="med-detail-section">
<h3>
{t("modal.intakeSchedule")}{" "}
{selectedMed.intakeRemindersEnabled && (
<span className="reminder-icon info-tooltip" data-tooltip={t("tooltips.intakeReminders")}>
🔔
</span>
)}
</h3>
<div className="med-detail-schedules">
{selectedMed.blisters.map((blister, idx) => {
// When using new intakes format with per-intake takenBy,
// each intake already represents one person's dose — don't multiply.
// For legacy intakes (no per-intake takenBy), multiply by personCount.
const intake = selectedMed.intakes?.[idx];
const hasPerIntakeTakenBy = !!intake?.takenBy;
const personCount = hasPerIntakeTakenBy ? 1 : Math.max(1, selectedMed.takenBy?.length || 1);
const totalUsage = blister.usage * personCount;
return (
<div key={idx} className="med-schedule-item">
<span className="med-schedule-usage">
{totalUsage} {totalUsage !== 1 ? t("common.pills") : t("common.pill")}
{selectedMed.pillWeightMg &&
` (${totalUsage * selectedMed.pillWeightMg} ${selectedMed.doseUnit ?? "mg"})`}
</span>
<span className="med-schedule-freq">
{t("form.blisters.every")} {blister.every}{" "}
{blister.every !== 1 ? t("common.days") : t("common.day")}
</span>
<span className="med-schedule-time">
{t("modal.at")}{" "}
{new Date(blister.start).toLocaleTimeString(getSystemLocale(i18n.language), {
hour: "2-digit",
minute: "2-digit",
})}
</span>
</div>
);
})}
</div>
</div>
)}
{/* Coverage Status Section */}
{medCoverage && status && (
<div className="med-detail-section">
<h3 className="section-header-with-badge">
{t("modal.coverageStatus")}
<span className={`status-chip small ${status.className}`}>{t(status.label)}</span>
</h3>
<div className="med-detail-grid">
<div className="med-detail-item">
<span className="med-detail-label">{t("modal.daysLeft")}</span>
<span className="med-detail-value">
{medCoverage.daysLeft !== null ? formatNumber(medCoverage.daysLeft) : "—"}
</span>
</div>
<div className="med-detail-item">
<span className="med-detail-label">{t("modal.runsOut")}</span>
<span className="med-detail-value">{medCoverage.depletionDate ?? "—"}</span>
</div>
</div>
</div>
)}
{/* Notes Section */}
{selectedMed.notes && (
<div className="med-detail-section">
<h3>📝 {t("modal.notes")}</h3>
<div className="med-notes-content">{selectedMed.notes}</div>
</div>
)}
{/* Refill History Section */}
{refillHistory.length > 0 && (
<div className="med-detail-section">
<h3
className="section-header-clickable"
onClick={() => onRefillHistoryExpandedChange(!refillHistoryExpanded)}
>
{t("refill.history")} ({refillHistory.length})
<span className="expand-arrow">{refillHistoryExpanded ? "▼" : "▶"}</span>
</h3>
{refillHistoryExpanded && (
<div className="refill-history-list">
{refillHistory.map((entry) => (
<div key={entry.id} className="refill-history-item">
<span className="refill-date">
{new Date(entry.refillDate).toLocaleDateString(getSystemLocale(i18n.language), {
day: "2-digit",
month: "short",
year: "numeric",
})}
,{" "}
{new Date(entry.refillDate).toLocaleTimeString(getSystemLocale(i18n.language), {
hour: "2-digit",
minute: "2-digit",
})}
</span>
<span className="refill-amount">
{(() => {
const total =
selectedMed.packageType === "bottle"
? entry.loosePillsAdded
: entry.packsAdded * selectedMed.blistersPerPack * selectedMed.pillsPerBlister +
entry.loosePillsAdded;
return `+${total} ${total === 1 ? t("common.pill") : t("common.pills")}`;
})()}
{entry.usedPrescription && (
<span className="refill-prescription-badge" title={t("refill.viaPrescription")}>
{" "}
📋
</span>
)}
</span>
</div>
))}
</div>
)}
</div>
)}
</div>
{/* Footer */}
<div className="med-detail-footer">
<button onClick={onClose}>{t("common.close")}</button>
<div className="footer-actions">
<button className="success" onClick={onOpenRefillModal}>
{t("refill.button")}
</button>
<button className="info" onClick={onOpenEditStockModal}>
{t("common.edit")}
</button>
{selectedMed.blisters.length > 0 && (
<button
className="secondary icon-only"
onClick={() => generateICS(selectedMed)}
title={t("modal.exportTooltip")}
>
📅
</button>
)}
</div>
</div>
</div>
{/* Image Lightbox */}
{showImageLightbox && selectedMed.imageUrl && (
<Lightbox src={`/api/images/${selectedMed.imageUrl}`} alt={selectedMed.name} onClose={onCloseImageLightbox} />
)}
{/* Refill Modal */}
{showRefillModal && (
<div
className="modal-overlay"
onClick={(e) => {
e.stopPropagation();
onCloseRefillModal();
}}
>
<div className="modal-content refill-modal" onClick={(e) => e.stopPropagation()}>
<button className="modal-close" onClick={onCloseRefillModal}>
×
</button>
<h2>{t("refill.title")}</h2>
<p className="refill-med-name">{selectedMed.name}</p>
<div className="refill-form">
{selectedMed.packageType === "blister" ? (
<>
<label>
{t("refill.packs")}
<input
type="number"
min="0"
value={refillPacks}
onChange={(e) => onRefillPacksChange(parseInt(e.target.value, 10) || 0)}
/>
</label>
<label>
{t("refill.loosePills")}
<input
type="number"
min="0"
value={refillLoose}
onChange={(e) => onRefillLooseChange(parseInt(e.target.value, 10) || 0)}
/>
</label>
</>
) : (
<label>
{t("refill.pillsToAdd")}
<input
type="number"
min="0"
value={refillLoose}
onChange={(e) => onRefillLooseChange(parseInt(e.target.value, 10) || 0)}
/>
</label>
)}
{selectedMed.prescriptionEnabled && (
<div className="refill-prescription-row full">
<label className="refill-prescription-toggle">
<input
type="checkbox"
checked={usePrescriptionRefill}
onChange={(e) => onUsePrescriptionRefillChange(e.target.checked)}
disabled={(Number(selectedMed.prescriptionRemainingRefills) || 0) <= 0}
/>
<span className="refill-prescription-label-text">{t("prescription.useForRefill")}</span>
</label>
<span className="refill-remaining-badge">
{t("prescription.remainingRefills")}: {Number(selectedMed.prescriptionRemainingRefills) || 0}
</span>
</div>
)}
</div>
<div className="modal-footer">
<button className="ghost" onClick={onCloseRefillModal}>
{t("common.cancel")}
</button>
<div className="refill-footer-right">
<button
className="success"
onClick={() => onSubmitRefill(selectedMed.id, usePrescriptionRefill)}
disabled={(refillPacks < 1 && refillLoose < 1) || refillSaving}
>
{refillSaving ? t("common.saving") : t("refill.button")}
</button>
{(() => {
const totalRefill =
selectedMed.packageType === "blister"
? refillPacks * selectedMed.blistersPerPack * selectedMed.pillsPerBlister + refillLoose
: refillLoose;
return totalRefill > 0 ? (
<span className="refill-preview">
+{totalRefill} {totalRefill === 1 ? t("common.pill") : t("common.pills")}
</span>
) : null;
})()}
</div>
</div>
</div>
</div>
)}
{/* Edit Stock Modal */}
{showEditStockModal && (
<div
className="modal-overlay"
onClick={(e) => {
e.stopPropagation();
onCloseEditStockModal();
}}
>
<div className="modal-content edit-stock-modal" onClick={(e) => e.stopPropagation()}>
<button className="modal-close" onClick={onCloseEditStockModal}>
×
</button>
<h2>{t("editStock.title")}</h2>
<p className="edit-stock-med-name">{selectedMed.name}</p>
<p className="edit-stock-hint">{t("editStock.hint")}</p>
{(() => {
const dbTotal = getMedTotal(selectedMed);
const currentTotal = medCoverage ? Math.round(medCoverage.medsLeft) : dbTotal;
const isBottle = selectedMed.packageType === "bottle";
const newTotal = isBottle
? editStockPartialBlisterPills
: editStockFullBlisters * selectedMed.pillsPerBlister + editStockPartialBlisterPills;
const difference = newTotal - currentTotal;
return (
<>
<div className="edit-stock-form">
{isBottle ? (
<label>
{t("editStock.totalPills")}
<input
type="number"
min="0"
value={editStockPartialInput}
onChange={(e) => {
const raw = e.target.value;
setEditStockPartialInput(raw);
onEditStockPartialBlisterPillsChange(raw === "" ? 0 : Math.max(0, parseStockInput(raw)));
}}
onBlur={() => {
const normalized = Math.max(0, parseStockInput(editStockPartialInput));
onEditStockPartialBlisterPillsChange(normalized);
setEditStockPartialInput(String(normalized));
}}
/>
</label>
) : (
<>
<label>
{t("editStock.fullBlisters")}{" "}
{t("editStock.pillsPerBlister", { count: selectedMed.pillsPerBlister })}
<input
type="number"
min="0"
value={editStockFullInput}
onChange={(e) => {
const raw = e.target.value;
setEditStockFullInput(raw);
onEditStockFullBlistersChange(raw === "" ? 0 : Math.max(0, parseStockInput(raw)));
}}
onBlur={() => {
const normalized = Math.max(0, parseStockInput(editStockFullInput));
onEditStockFullBlistersChange(normalized);
setEditStockFullInput(String(normalized));
}}
/>
</label>
<label>
{t("editStock.partialBlisterPills")}
<input
type="number"
min={fullForBounds > 0 ? -(selectedMed.pillsPerBlister - 1) : 0}
max={selectedMed.pillsPerBlister}
value={editStockPartialInput}
onChange={(e) => {
const raw = e.target.value;
setEditStockPartialInput(raw);
if (raw === "") {
onEditStockPartialBlisterPillsChange(0);
return;
}
const val = parseStockInput(raw);
const min = fullForBounds > 0 ? -(selectedMed.pillsPerBlister - 1) : 0;
const max = selectedMed.pillsPerBlister;
onEditStockPartialBlisterPillsChange(Math.max(min, Math.min(val, max)));
}}
onBlur={() => {
const min = fullForBounds > 0 ? -(selectedMed.pillsPerBlister - 1) : 0;
const max = selectedMed.pillsPerBlister;
const normalized = Math.max(min, Math.min(parseStockInput(editStockPartialInput), max));
onEditStockPartialBlisterPillsChange(normalized);
setEditStockPartialInput(String(normalized));
}}
/>
</label>
</>
)}
</div>
<div className="edit-stock-summary">
<div className="summary-row">
<span>{t("editStock.currentTotal")}:</span>
<span>
{currentTotal} {currentTotal === 1 ? t("common.pill") : t("common.pills")}
</span>
</div>
<div className="summary-row">
<span>{t("editStock.newTotal")}:</span>
<span>
{newTotal} {newTotal === 1 ? t("common.pill") : t("common.pills")}
</span>
</div>
<div
className={`summary-row difference ${difference > 0 ? "positive" : difference < 0 ? "negative" : ""}`}
>
<span>{t("editStock.difference")}:</span>
<span>
{difference > 0 ? "+" : ""}
{difference} {Math.abs(difference) === 1 ? t("common.pill") : t("common.pills")}
</span>
</div>
</div>
</>
);
})()}
<div className="modal-footer">
<button className="ghost" onClick={onCloseEditStockModal}>
{t("common.cancel")}
</button>
<button
className="info"
onClick={() => onSubmitStockCorrection(selectedMed.id)}
disabled={editStockSaving}
>
{editStockSaving ? t("editStock.saving") : t("editStock.save")}
</button>
</div>
</div>
</div>
)}
</div>
);
}