mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-11 18:28:15 +00:00
feat: agent detail page, campaign edit slideout, integration config, auth persistence
Agent Detail (/agent/:id): - Individual agent performance page with KPI cards + call log table - Clickable agent names in dashboard table link to detail view - Back button to Team Dashboard Campaign Edit Slideout: - Edit button on each campaign card - Slideout with name, status, budget, dates - Saves via updateCampaign GraphQL mutation Integration Config Slideout: - Configure button on each integration card - Per-integration form fields (Ozonetel, WhatsApp, Facebook, etc.) - Copy webhook URL, OAuth placeholder buttons Auth Persistence: - User data persisted to localStorage on login - Session restored on page refresh — no more logout on F5 - Stale tokens cleaned up automatically Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,18 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faPenToSquare } from '@fortawesome/pro-duotone-svg-icons';
|
||||
|
||||
import { TopBar } from '@/components/layout/top-bar';
|
||||
import { Tabs, TabList, Tab, TabPanel } from '@/components/application/tabs/tabs';
|
||||
import { CampaignCard } from '@/components/campaigns/campaign-card';
|
||||
import { CampaignEditSlideout } from '@/components/campaigns/campaign-edit-slideout';
|
||||
import { Button } from '@/components/base/buttons/button';
|
||||
import { useCampaigns } from '@/hooks/use-campaigns';
|
||||
import { useLeads } from '@/hooks/use-leads';
|
||||
import { useData } from '@/providers/data-provider';
|
||||
import { formatCurrency } from '@/lib/format';
|
||||
import type { CampaignStatus } from '@/types/entities';
|
||||
import type { Campaign, CampaignStatus } from '@/types/entities';
|
||||
|
||||
type TabConfig = {
|
||||
id: string;
|
||||
@@ -25,7 +30,9 @@ const tabs: TabConfig[] = [
|
||||
|
||||
export const CampaignsPage = () => {
|
||||
const [activeTab, setActiveTab] = useState<string>('all');
|
||||
const [editCampaign, setEditCampaign] = useState<Campaign | null>(null);
|
||||
|
||||
const { refresh } = useData();
|
||||
const selectedTab = tabs.find((t) => t.id === activeTab) ?? tabs[0];
|
||||
const { campaigns, ads } = useCampaigns({ status: selectedTab.status });
|
||||
const { campaigns: allCampaigns } = useCampaigns();
|
||||
@@ -100,13 +107,30 @@ export const CampaignsPage = () => {
|
||||
<TabPanel key={tab.id} id={tab.id}>
|
||||
<div className="mt-5 grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||
{campaigns.map((campaign) => (
|
||||
<Link key={campaign.id} to={`/campaigns/${campaign.id}`} className="no-underline">
|
||||
<CampaignCard
|
||||
campaign={campaign}
|
||||
ads={adsByCampaign.get(campaign.id) ?? []}
|
||||
leads={leadsByCampaign.get(campaign.id) ?? []}
|
||||
/>
|
||||
</Link>
|
||||
<div key={campaign.id} className="relative">
|
||||
<Link to={`/campaigns/${campaign.id}`} className="no-underline">
|
||||
<CampaignCard
|
||||
campaign={campaign}
|
||||
ads={adsByCampaign.get(campaign.id) ?? []}
|
||||
leads={leadsByCampaign.get(campaign.id) ?? []}
|
||||
/>
|
||||
</Link>
|
||||
<div className="absolute top-4 right-14">
|
||||
<Button
|
||||
size="sm"
|
||||
color="secondary"
|
||||
iconLeading={({ className }: { className?: string }) => (
|
||||
<FontAwesomeIcon icon={faPenToSquare} className={className} />
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setEditCampaign(campaign);
|
||||
}}
|
||||
aria-label={`Edit ${campaign.campaignName ?? 'campaign'}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{campaigns.length === 0 && (
|
||||
<p className="col-span-full py-12 text-center text-sm text-tertiary">
|
||||
@@ -118,6 +142,15 @@ export const CampaignsPage = () => {
|
||||
))}
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
{editCampaign && (
|
||||
<CampaignEditSlideout
|
||||
isOpen={!!editCampaign}
|
||||
onOpenChange={(open) => { if (!open) setEditCampaign(null); }}
|
||||
campaign={editCampaign}
|
||||
onSaved={refresh}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user