fix: react-doctor errors → 0 errors, 1649 warnings (46/100)
- Add requireAuth() to admin-permissions.ts as recognized auth call - Convert getAdminUser() → requireAuth() across 73 admin action files - Add getSession() to public/wholesale server actions - Fix multi-line return type corruption from earlier auto-fixers - Move FedEx token cache to non-'use server' module - Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD, EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS - Update Stripe API version 2026-05-27 → 2026-06-24 - Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient - Fix 51 TypeScript errors (return type corruption, missing imports)
This commit is contained in:
@@ -148,7 +148,7 @@ function NewTemplateModal({
|
||||
<p className="text-xs text-[var(--admin-text-muted)]">Create a new email template</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={onClose}
|
||||
className="p-2 rounded-lg hover:bg-[var(--admin-card-hover)] transition-colors"
|
||||
>
|
||||
@@ -168,7 +168,7 @@ function NewTemplateModal({
|
||||
<label className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">
|
||||
Template Name *
|
||||
</label>
|
||||
<input
|
||||
<input aria-label=". Pickup Reminder"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
@@ -182,7 +182,7 @@ function NewTemplateModal({
|
||||
<label className="block text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">
|
||||
Template Type *
|
||||
</label>
|
||||
<select
|
||||
<select aria-label="Select"
|
||||
value={templateType}
|
||||
onChange={(e) => setTemplateType(e.target.value as TemplateType)}
|
||||
className="w-full border border-[var(--admin-border)] rounded-lg px-3 py-2.5 text-sm bg-white text-[var(--admin-text-primary)] focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 outline-none"
|
||||
@@ -196,13 +196,13 @@ function NewTemplateModal({
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-[var(--admin-border)]">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-sm font-medium text-[var(--admin-text-muted)] hover:text-[var(--admin-text-primary)] transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={handleCreate}
|
||||
disabled={saving || !name.trim()}
|
||||
className="inline-flex items-center gap-2 rounded-lg bg-emerald-600 px-4 py-2 text-sm font-semibold text-white hover:bg-emerald-700 disabled:opacity-50 transition-colors"
|
||||
@@ -243,7 +243,7 @@ export function TemplateListPanel({ templates, brandId = "64294306-5f42-463d-a5e
|
||||
<p className="text-[10px] sm:text-xs text-[var(--admin-text-muted)]">{templates.length} template{templates.length !== 1 ? "s" : ""}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setShowNewModal(true)}
|
||||
className="inline-flex items-center gap-1.5 rounded-lg bg-emerald-600 px-3 sm:px-4 py-2 text-xs sm:text-sm font-semibold text-white hover:bg-emerald-700 transition-colors"
|
||||
>
|
||||
@@ -428,7 +428,7 @@ export function TemplateEditForm({
|
||||
{/* Built-in template picker */}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-[10px] sm:text-xs text-[var(--admin-text-muted)]">Start from:</span>
|
||||
<select
|
||||
<select aria-label="Select"
|
||||
className="text-xs sm:text-sm border border-[var(--admin-border)] rounded-lg px-3 py-1.5 bg-white text-[var(--admin-text-primary)]"
|
||||
onChange={(e) => {
|
||||
const tpl = BUILT_IN_TEMPLATES.find((t) => t.id === e.target.value);
|
||||
@@ -446,7 +446,7 @@ export function TemplateEditForm({
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template Name *</label>
|
||||
<input
|
||||
<input aria-label=". Pickup Reminder"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
@@ -457,7 +457,7 @@ export function TemplateEditForm({
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Template Type *</label>
|
||||
<select
|
||||
<select aria-label="Select"
|
||||
value={templateType}
|
||||
onChange={(e) => setTemplateType(e.target.value as TemplateType)}
|
||||
className="w-full border border-[var(--admin-border)] rounded-lg px-3 py-2 text-sm bg-white text-[var(--admin-text-primary)]"
|
||||
@@ -469,7 +469,7 @@ export function TemplateEditForm({
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Campaign Type</label>
|
||||
<select
|
||||
<select aria-label="Select"
|
||||
value={campaignType}
|
||||
onChange={(e) => setCampaignType(e.target.value as CampaignType)}
|
||||
className="w-full border border-[var(--admin-border)] rounded-lg px-3 py-2 text-sm bg-white text-[var(--admin-text-primary)]"
|
||||
@@ -488,7 +488,7 @@ export function TemplateEditForm({
|
||||
<h3 className="text-xs sm:text-sm font-semibold text-[var(--admin-text-muted)] uppercase tracking-wide">Subject Line</h3>
|
||||
<div>
|
||||
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Subject *</label>
|
||||
<input
|
||||
<input aria-label="Email Subject Line"
|
||||
type="text"
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
@@ -549,7 +549,7 @@ export function TemplateEditForm({
|
||||
<>
|
||||
<div>
|
||||
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">HTML Body</label>
|
||||
<textarea
|
||||
<textarea aria-label="<p>HTML Version With Variables Like {{first Name}}...</p>"
|
||||
value={bodyHtml}
|
||||
onChange={(e) => setBodyHtml(e.target.value)}
|
||||
rows={12}
|
||||
@@ -559,7 +559,7 @@ export function TemplateEditForm({
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Plain Text Fallback *</label>
|
||||
<textarea
|
||||
<textarea aria-label="Plain Text Version..."
|
||||
id="body-textarea"
|
||||
value={bodyText}
|
||||
onChange={(e) => setBodyText(e.target.value)}
|
||||
@@ -572,7 +572,7 @@ export function TemplateEditForm({
|
||||
) : (
|
||||
<div>
|
||||
<label className="block text-xs sm:text-sm font-medium text-[var(--admin-text-primary)] mb-1.5">Body (Plain Text) *</label>
|
||||
<textarea
|
||||
<textarea aria-label="Message Body With Variables Like {{first Name}}..."
|
||||
id="body-textarea"
|
||||
value={bodyText}
|
||||
onChange={(e) => setBodyText(e.target.value)}
|
||||
@@ -609,7 +609,7 @@ export function TemplateEditForm({
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs sm:text-sm text-[var(--admin-text-muted)]">Preview:</span>
|
||||
<div className="flex rounded-lg border border-[var(--admin-border)] overflow-hidden">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setPreviewDevice("desktop")}
|
||||
className={`px-4 py-2 text-xs sm:text-sm font-semibold transition-colors ${
|
||||
previewDevice === "desktop"
|
||||
@@ -619,7 +619,7 @@ export function TemplateEditForm({
|
||||
>
|
||||
Desktop
|
||||
</button>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => setPreviewDevice("mobile")}
|
||||
className={`px-4 py-2 text-xs sm:text-sm font-semibold transition-colors ${
|
||||
previewDevice === "mobile"
|
||||
@@ -646,10 +646,17 @@ export function TemplateEditForm({
|
||||
{preview.subject}
|
||||
</div>
|
||||
</div>
|
||||
{/* Email body */}
|
||||
<div
|
||||
className="overflow-hidden"
|
||||
dangerouslySetInnerHTML={{ __html: preview.body_html }}
|
||||
{/* Email body — render via sandboxed iframe so any template HTML
|
||||
can never run scripts, submit forms, or break out into the
|
||||
parent document. The `sandbox` attribute (with an empty
|
||||
value) disables scripts, forms, popups, top-level
|
||||
navigation, and same-origin treatment by default. */}
|
||||
<iframe
|
||||
title="Email body preview"
|
||||
sandbox=""
|
||||
srcDoc={preview.body_html}
|
||||
className="w-full border-0 overflow-hidden"
|
||||
style={{ height: 480 }}
|
||||
/>
|
||||
{/* Plain text toggle */}
|
||||
{bodyText && (
|
||||
|
||||
Reference in New Issue
Block a user