Check payload logging
This commit is contained in:
parent
ab7944e866
commit
1e9947f88c
|
|
@ -36,7 +36,7 @@ function SubLayout({ children }) {
|
||||||
// Guard: redirect to / if no active business in session.
|
// Guard: redirect to / if no active business in session.
|
||||||
// Also enforce cURL-first: only the cURL profile route is available until an active profile exists.
|
// Also enforce cURL-first: only the cURL profile route is available until an active profile exists.
|
||||||
function BusinessGuard({ children, isGlobalSmsRoute }) {
|
function BusinessGuard({ children, isGlobalSmsRoute }) {
|
||||||
const { activeBusinessId, loading, hasGlobalSms } = useBusiness();
|
const { activeBusinessId, loading, isSetupComplete } = useBusiness();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
|
@ -51,7 +51,7 @@ function BusinessGuard({ children, isGlobalSmsRoute }) {
|
||||||
return <Navigate to="/" state={{ from: location }} replace />;
|
return <Navigate to="/" state={{ from: location }} replace />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasGlobalSms && !isGlobalSmsRoute) {
|
if (!isSetupComplete && !isGlobalSmsRoute) {
|
||||||
return <Navigate to={`/${activeBusinessId}/global-sms`} replace />;
|
return <Navigate to={`/${activeBusinessId}/global-sms`} replace />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export default function Sidebar() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ id: 'globalSms', to: `/${activeBusinessId}/global-sms`, label: 'Global SMS cURL' },
|
{ id: 'globalSms', to: `/${activeBusinessId}/global-sms`, label: 'Omni-channel SMS' },
|
||||||
{ id: 'events', to: `/${activeBusinessId}/events`, label: 'Events' },
|
{ id: 'events', to: `/${activeBusinessId}/events`, label: 'Events' },
|
||||||
{ id: 'templates', to: `/${activeBusinessId}/templates`, label: 'Templates' },
|
{ id: 'templates', to: `/${activeBusinessId}/templates`, label: 'Templates' },
|
||||||
];
|
];
|
||||||
|
|
@ -70,8 +70,7 @@ export default function Sidebar() {
|
||||||
key={id}
|
key={id}
|
||||||
to={to}
|
to={to}
|
||||||
className={({ isActive }) =>
|
className={({ isActive }) =>
|
||||||
`flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors duration-150 ${
|
`flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors duration-150 ${isActive
|
||||||
isActive
|
|
||||||
? 'bg-refresh-hover text-primary-blue'
|
? 'bg-refresh-hover text-primary-blue'
|
||||||
: 'text-text-muted hover:text-text-primary hover:bg-row-hover'
|
: 'text-text-muted hover:text-text-primary hover:bg-row-hover'
|
||||||
}`
|
}`
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,16 @@ const SESSION_KEY = 'sms_active_business';
|
||||||
export function BusinessProvider({ children }) {
|
export function BusinessProvider({ children }) {
|
||||||
const [activeBusiness, setActiveBusinessState] = useState(null);
|
const [activeBusiness, setActiveBusinessState] = useState(null);
|
||||||
const [hasGlobalSms, setHasGlobalSms] = useState(false);
|
const [hasGlobalSms, setHasGlobalSms] = useState(false);
|
||||||
|
const [isSetupComplete, setIsSetupComplete] = useState(false);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const updateReadyState = useCallback((activeProfile) => {
|
||||||
|
const hasProfile = !!activeProfile;
|
||||||
|
setHasGlobalSms(hasProfile);
|
||||||
|
const p = activeProfile?.provider || {};
|
||||||
|
setIsSetupComplete(hasProfile && !!p.providerName && !!p.senderId && !!p.dltEntityId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// On mount: rehydrate from sessionStorage and refresh from API
|
// On mount: rehydrate from sessionStorage and refresh from API
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function rehydrate() {
|
async function rehydrate() {
|
||||||
|
|
@ -30,7 +38,7 @@ export function BusinessProvider({ children }) {
|
||||||
apiClient.get(`/api/businesses/${businessId}/global-sms/active`).catch(() => ({ data: {} }))
|
apiClient.get(`/api/businesses/${businessId}/global-sms/active`).catch(() => ({ data: {} }))
|
||||||
]);
|
]);
|
||||||
setActiveBusinessState(bizRes.data);
|
setActiveBusinessState(bizRes.data);
|
||||||
setHasGlobalSms(!!smsRes.data?.activeProfile);
|
updateReadyState(smsRes.data?.activeProfile);
|
||||||
sessionStorage.setItem(SESSION_KEY, JSON.stringify({
|
sessionStorage.setItem(SESSION_KEY, JSON.stringify({
|
||||||
businessId,
|
businessId,
|
||||||
companyId: runtimeCompanyId || companyId || '',
|
companyId: runtimeCompanyId || companyId || '',
|
||||||
|
|
@ -40,6 +48,7 @@ export function BusinessProvider({ children }) {
|
||||||
sessionStorage.removeItem(SESSION_KEY);
|
sessionStorage.removeItem(SESSION_KEY);
|
||||||
setActiveBusinessState(null);
|
setActiveBusinessState(null);
|
||||||
setHasGlobalSms(false);
|
setHasGlobalSms(false);
|
||||||
|
setIsSetupComplete(false);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -55,15 +64,17 @@ export function BusinessProvider({ children }) {
|
||||||
}));
|
}));
|
||||||
try {
|
try {
|
||||||
const smsRes = await apiClient.get(`/api/businesses/${business.businessId}/global-sms/active`);
|
const smsRes = await apiClient.get(`/api/businesses/${business.businessId}/global-sms/active`);
|
||||||
setHasGlobalSms(!!smsRes.data?.activeProfile);
|
updateReadyState(smsRes.data?.activeProfile);
|
||||||
} catch {
|
} catch {
|
||||||
setHasGlobalSms(false);
|
setHasGlobalSms(false);
|
||||||
|
setIsSetupComplete(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const clearBusiness = useCallback(() => {
|
const clearBusiness = useCallback(() => {
|
||||||
setActiveBusinessState(null);
|
setActiveBusinessState(null);
|
||||||
setHasGlobalSms(false);
|
setHasGlobalSms(false);
|
||||||
|
setIsSetupComplete(false);
|
||||||
sessionStorage.removeItem(SESSION_KEY);
|
sessionStorage.removeItem(SESSION_KEY);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -71,7 +82,7 @@ export function BusinessProvider({ children }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BusinessContext.Provider value={{
|
<BusinessContext.Provider value={{
|
||||||
activeBusiness, activeBusinessId, setActiveBusiness, clearBusiness, loading, hasGlobalSms, setHasGlobalSms
|
activeBusiness, activeBusinessId, setActiveBusiness, clearBusiness, loading, hasGlobalSms, setHasGlobalSms, isSetupComplete, setIsSetupComplete
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</BusinessContext.Provider>
|
</BusinessContext.Provider>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import apiClient from '../api/client';
|
import apiClient from '../api/client';
|
||||||
import { useBusiness } from '../context/BusinessContext';
|
import { useBusiness } from '../context/BusinessContext';
|
||||||
|
|
||||||
export default function GlobalSms() {
|
export default function GlobalSms() {
|
||||||
const { businessId } = useParams();
|
const { businessId } = useParams();
|
||||||
const { setHasGlobalSms } = useBusiness();
|
const navigate = useNavigate();
|
||||||
|
const { isSetupComplete, setHasGlobalSms, setIsSetupComplete } = useBusiness();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [profiles, setProfiles] = useState([]);
|
const [profiles, setProfiles] = useState([]);
|
||||||
const [activeProfileId, setActiveProfileId] = useState(null);
|
const [activeProfileId, setActiveProfileId] = useState(null);
|
||||||
|
|
@ -14,30 +15,56 @@ export default function GlobalSms() {
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [success, setSuccess] = useState('');
|
const [success, setSuccess] = useState('');
|
||||||
|
|
||||||
// Form state for Create / Edit
|
// Form state for Create / Edit Profile
|
||||||
const [editingId, setEditingId] = useState(null);
|
const [editingId, setEditingId] = useState(null);
|
||||||
const [formName, setFormName] = useState('');
|
const [formName, setFormName] = useState('');
|
||||||
const [formCurl, setFormCurl] = useState('');
|
const [formCurl, setFormCurl] = useState('');
|
||||||
const [formSetActive, setFormSetActive] = useState(true);
|
const [formSetActive, setFormSetActive] = useState(true);
|
||||||
|
|
||||||
|
// Form state for Missing Provider Fields
|
||||||
|
const [providerForm, setProviderForm] = useState({ providerName: '', senderId: '', dltEntityId: '' });
|
||||||
|
const [savingProvider, setSavingProvider] = useState(false);
|
||||||
|
|
||||||
const loadProfiles = useCallback(async () => {
|
const loadProfiles = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await apiClient.get(`/api/businesses/${businessId}/global-sms/profiles`);
|
const res = await apiClient.get(`/api/businesses/${businessId}/global-sms/profiles`);
|
||||||
setProfiles(res.data.profiles || []);
|
const fetchedProfiles = res.data.profiles || [];
|
||||||
setActiveProfileId(res.data.activeProfileId);
|
const fetchActiveId = res.data.activeProfileId;
|
||||||
setHasGlobalSms(!!res.data.activeProfileId);
|
setProfiles(fetchedProfiles);
|
||||||
|
setActiveProfileId(fetchActiveId);
|
||||||
|
|
||||||
|
const activeProfile = fetchedProfiles.find(p => p.id === fetchActiveId) || null;
|
||||||
|
const hasProfile = !!activeProfile;
|
||||||
|
setHasGlobalSms(hasProfile);
|
||||||
|
|
||||||
|
const p = activeProfile?.provider || {};
|
||||||
|
const complete = hasProfile && !!p.providerName && !!p.senderId && !!p.dltEntityId;
|
||||||
|
setIsSetupComplete(complete);
|
||||||
|
|
||||||
|
setProviderForm({
|
||||||
|
providerName: p.providerName || '',
|
||||||
|
senderId: p.senderId || '',
|
||||||
|
dltEntityId: p.dltEntityId || '',
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
setError('Failed to load cURL profiles');
|
setError('Failed to load cURL profiles');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [businessId, setHasGlobalSms]);
|
}, [businessId, setHasGlobalSms, setIsSetupComplete]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadProfiles();
|
loadProfiles();
|
||||||
}, [loadProfiles]);
|
}, [loadProfiles]);
|
||||||
|
|
||||||
|
const activeProfile = profiles.find(p => p.id === activeProfileId) || null;
|
||||||
|
const pData = activeProfile?.provider || {};
|
||||||
|
const missingFields = [];
|
||||||
|
if (activeProfile && !pData.providerName) missingFields.push('providerName');
|
||||||
|
if (activeProfile && !pData.senderId) missingFields.push('senderId');
|
||||||
|
if (activeProfile && !pData.dltEntityId) missingFields.push('dltEntityId');
|
||||||
|
|
||||||
function handleAddClick() {
|
function handleAddClick() {
|
||||||
setEditingId(null);
|
setEditingId(null);
|
||||||
setFormName('');
|
setFormName('');
|
||||||
|
|
@ -51,7 +78,7 @@ export default function GlobalSms() {
|
||||||
setEditingId(profile.id);
|
setEditingId(profile.id);
|
||||||
setFormName(profile.name);
|
setFormName(profile.name);
|
||||||
setFormCurl(profile.rawCurl);
|
setFormCurl(profile.rawCurl);
|
||||||
setFormSetActive(false); // only matters for create
|
setFormSetActive(false);
|
||||||
setError('');
|
setError('');
|
||||||
setSuccess('');
|
setSuccess('');
|
||||||
}
|
}
|
||||||
|
|
@ -108,6 +135,30 @@ export default function GlobalSms() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleProviderSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!activeProfileId) return;
|
||||||
|
setSavingProvider(true);
|
||||||
|
setError('');
|
||||||
|
setSuccess('');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apiClient.patch(`/api/businesses/${businessId}/global-sms/profiles/${activeProfileId}`, {
|
||||||
|
provider: {
|
||||||
|
providerName: providerForm.providerName,
|
||||||
|
senderId: providerForm.senderId.toUpperCase(),
|
||||||
|
dltEntityId: providerForm.dltEntityId,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setSuccess('Provider details saved successfully!');
|
||||||
|
await loadProfiles();
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.response?.data?.error || 'Failed to save provider details');
|
||||||
|
} finally {
|
||||||
|
setSavingProvider(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-64 items-center justify-center">
|
<div className="flex h-64 items-center justify-center">
|
||||||
|
|
@ -118,12 +169,37 @@ export default function GlobalSms() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto space-y-8 pb-12">
|
<div className="max-w-4xl mx-auto space-y-8 pb-12">
|
||||||
{/* Header */}
|
{/* Header & Stepper */}
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold text-text-primary mb-2">cURL Profiles</h2>
|
<h2 className="text-2xl font-bold text-text-primary mb-2">Setup configuration</h2>
|
||||||
<p className="text-sm text-text-muted">
|
<p className="text-sm text-text-muted mb-8">
|
||||||
Manage the cURL commands used to generate and test SMS templates. The active profile will be used across the application.
|
Complete this flow to configure your cURL profile and brand provider data. You must finish setup before generating templates.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* CSS Stepper */}
|
||||||
|
<div className="flex items-center w-full justify-between relative before:absolute before:top-4 before:left-0 before:h-[2px] before:w-full before:bg-border-soft before:-z-10">
|
||||||
|
{[
|
||||||
|
{ label: 'Add / Select Profile', done: !!activeProfile, active: !activeProfile },
|
||||||
|
{ label: 'Validate cURL', done: !!activeProfile, active: false },
|
||||||
|
{ label: 'Complete Fields', done: isSetupComplete, active: !!activeProfile && !isSetupComplete },
|
||||||
|
{ label: 'Ready', done: isSetupComplete, active: isSetupComplete }
|
||||||
|
].map((step, idx) => (
|
||||||
|
<div key={idx} className="flex flex-col items-center">
|
||||||
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm transition-colors border-2 ${step.done
|
||||||
|
? 'bg-primary-blue border-primary-blue text-white'
|
||||||
|
: step.active
|
||||||
|
? 'bg-white border-primary-blue text-primary-blue'
|
||||||
|
: 'bg-white border-border-main text-text-muted'
|
||||||
|
}`}>
|
||||||
|
{step.done ? '✓' : idx + 1}
|
||||||
|
</div>
|
||||||
|
<span className={`mt-2 text-[11px] uppercase tracking-wide font-bold ${step.done || step.active ? 'text-primary-blue' : 'text-text-muted'
|
||||||
|
}`}>
|
||||||
|
{step.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
|
|
@ -139,8 +215,105 @@ export default function GlobalSms() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Active Profile Setup Review Block */}
|
||||||
|
{activeProfile && (
|
||||||
|
<div className={`p-6 rounded-xl border ${isSetupComplete ? 'border-primary-blue bg-[#f0f2fb]' : 'border-[#d47f45] bg-[#fdfaf5]'} shadow-sm`}>
|
||||||
|
<div className="flex items-center gap-3 mb-4">
|
||||||
|
<h3 className="font-bold text-text-primary text-lg">Active Setup: {activeProfile.name}</h3>
|
||||||
|
{isSetupComplete ? (
|
||||||
|
<span className="px-3 py-1 bg-badge-bg text-badge-text border border-badge-border rounded-full text-xs font-bold uppercase tracking-wide">Setup Complete</span>
|
||||||
|
) : (
|
||||||
|
<span className="px-3 py-1 bg-tags-bg text-tags-text border border-tags-border rounded-full text-xs font-bold uppercase tracking-wide">Missing Information</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
<div className="space-y-3">
|
||||||
|
<p className="text-sm font-medium text-text-primary">Parsed Provider Data:</p>
|
||||||
|
<ul className="space-y-2 text-sm">
|
||||||
|
<li className="flex justify-between items-center bg-surface-white p-2 rounded border border-border-soft">
|
||||||
|
<span className="text-text-muted">Provider:</span>
|
||||||
|
<span className="font-bold text-text-primary">{pData.providerName || <span className="text-error-text text-xs uppercase">Missing</span>}</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex justify-between items-center bg-surface-white p-2 rounded border border-border-soft">
|
||||||
|
<span className="text-text-muted">Sender ID:</span>
|
||||||
|
<span className="font-bold text-text-primary">{pData.senderId || <span className="text-error-text text-xs uppercase">Missing</span>}</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex justify-between items-center bg-surface-white p-2 rounded border border-border-soft">
|
||||||
|
<span className="text-text-muted">Entity ID:</span>
|
||||||
|
<span className="font-bold text-text-primary">{pData.dltEntityId || <span className="text-error-text text-xs uppercase">Missing</span>}</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex justify-between items-center bg-surface-white p-2 rounded border border-border-soft">
|
||||||
|
<span className="text-text-muted">Auth Key:</span>
|
||||||
|
<span className="font-semibold text-text-primary font-mono">{pData.authKey ? '••••••••' : 'None setup'}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!isSetupComplete && (
|
||||||
|
<div className="bg-surface-white p-4 rounded-lg border border-[#d47f45]/30">
|
||||||
|
<p className="text-sm font-semibold text-text-primary mb-3">Please fill in the missing fields:</p>
|
||||||
|
<form onSubmit={handleProviderSubmit} className="space-y-3">
|
||||||
|
{missingFields.includes('providerName') && (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Provider Name (e.g. MSG91)"
|
||||||
|
value={providerForm.providerName}
|
||||||
|
onChange={e => setProviderForm({ ...providerForm, providerName: e.target.value })}
|
||||||
|
className="w-full px-3 py-2 border border-border-main rounded text-sm focus:ring-1 focus:ring-primary-blue bg-page-bg"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{missingFields.includes('senderId') && (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Sender ID (6 letters)"
|
||||||
|
maxLength={6}
|
||||||
|
value={providerForm.senderId}
|
||||||
|
onChange={e => setProviderForm({ ...providerForm, senderId: e.target.value.toUpperCase() })}
|
||||||
|
className="w-full px-3 py-2 border border-border-main rounded text-sm focus:ring-1 focus:ring-primary-blue bg-page-bg uppercase"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{missingFields.includes('dltEntityId') && (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="19-digit DLT PE ID"
|
||||||
|
value={providerForm.dltEntityId}
|
||||||
|
onChange={e => setProviderForm({ ...providerForm, dltEntityId: e.target.value })}
|
||||||
|
className="w-full px-3 py-2 border border-border-main rounded text-sm focus:ring-1 focus:ring-primary-blue bg-page-bg"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={savingProvider}
|
||||||
|
className="w-full py-2 bg-[#d47f45] hover:bg-[#cf7b3f] text-white text-sm font-bold rounded shadow-sm transition disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{savingProvider ? 'Saving...' : 'Save Required Details'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isSetupComplete && (
|
||||||
|
<div className="flex flex-col justify-center items-center h-full space-y-4">
|
||||||
|
<p className="text-center text-sm font-medium text-text-muted">Your active cURL profile is fully configured.</p>
|
||||||
|
<button
|
||||||
|
onClick={() => navigate(`/${businessId}/events`)}
|
||||||
|
className="px-6 py-3 bg-primary-blue hover:bg-primary-dark text-white rounded-lg shadow font-semibold text-sm transition w-full"
|
||||||
|
>
|
||||||
|
Continue to Events →
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Profiles List */}
|
{/* Profiles List */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4 pt-4 border-t border-border-soft">
|
||||||
|
<h3 className="font-bold text-text-primary text-lg">All Profiles</h3>
|
||||||
{profiles.length > 0 ? (
|
{profiles.length > 0 ? (
|
||||||
profiles.map(p => {
|
profiles.map(p => {
|
||||||
const isActive = p.id === activeProfileId;
|
const isActive = p.id === activeProfileId;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user