fix: BB admin login no longer bounces to distribute
Problem: /admin/* pages redirect unauthed users to /login or /account,
both of which then hit middleware redirecting to distribute.relevant
mediaproperties.com. Distribute is on a different domain, so its session
cookie doesn't carry to broadcastbeat.com — sign-in there left admins
stranded on distribute's dashboard.
Fix:
- Remove /login (and /register) from middleware REDIRECT_PATHS. wp-login,
wp-admin, client-login still bounce to distribute.
- /login is now a real BB sign-in form (was a redirect to /client-login).
- Admin pages preserve return path via /login?next=<encoded path>.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,7 +39,7 @@ export default function ClientsPage() {
|
|||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||||
const [successMsg, setSuccessMsg] = useState('');
|
const [successMsg, setSuccessMsg] = useState('');
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function CommissionsPage() {
|
|||||||
const [filterStaff, setFilterStaff] = useState('');
|
const [filterStaff, setFilterStaff] = useState('');
|
||||||
const [staff, setStaff] = useState<any[]>([]);
|
const [staff, setStaff] = useState<any[]>([]);
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default function DocumentsPage() {
|
|||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [form, setForm] = useState({ document_type: 'sales_agreement', title: '', year: String(new Date().getFullYear()), person: '', file_url: '', notes: '' });
|
const [form, setForm] = useState({ document_type: 'sales_agreement', title: '', year: String(new Date().getFullYear()), person: '', file_url: '', notes: '' });
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default function ExpensesPage() {
|
|||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||||
const [successMsg, setSuccessMsg] = useState('');
|
const [successMsg, setSuccessMsg] = useState('');
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function InvoicesContent() {
|
|||||||
const [payErrors, setPayErrors] = useState<Record<string, string>>({});
|
const [payErrors, setPayErrors] = useState<Record<string, string>>({});
|
||||||
const [successMsg, setSuccessMsg] = useState('');
|
const [successMsg, setSuccessMsg] = useState('');
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function OrdersContent() {
|
|||||||
invoicing_schedule: 'single', next_invoice_date: '', staff_id: '', po_number: '', notes: '',
|
invoicing_schedule: 'single', next_invoice_date: '', staff_id: '', po_number: '', notes: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function AccountingDashboard() {
|
|||||||
const [loadingData, setLoadingData] = useState(true);
|
const [loadingData, setLoadingData] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.push('/login');
|
if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function ReconciliationPage() {
|
|||||||
const [uploading, setUploading] = useState(false);
|
const [uploading, setUploading] = useState(false);
|
||||||
const [summary, setSummary] = useState<{ matched: number; unmatched: number } | null>(null);
|
const [summary, setSummary] = useState<{ matched: number; unmatched: number } | null>(null);
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function ReportsPage() {
|
|||||||
const [startDate, setStartDate] = useState(`${new Date().getFullYear()}-01-01`);
|
const [startDate, setStartDate] = useState(`${new Date().getFullYear()}-01-01`);
|
||||||
const [endDate, setEndDate] = useState(new Date().toISOString().split('T')[0]);
|
const [endDate, setEndDate] = useState(new Date().toISOString().split('T')[0]);
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
async function loadReport() {
|
async function loadReport() {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default function StaffPage() {
|
|||||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||||
const [successMsg, setSuccessMsg] = useState('');
|
const [successMsg, setSuccessMsg] = useState('');
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function AdOpsIOsPage() {
|
|||||||
page_url: '', newsletter_issue_date: '',
|
page_url: '', newsletter_issue_date: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export default function AdOpsNotificationsPage() {
|
|||||||
const [notifications, setNotifications] = useState<any[]>([]);
|
const [notifications, setNotifications] = useState<any[]>([]);
|
||||||
const [loadingData, setLoadingData] = useState(true);
|
const [loadingData, setLoadingData] = useState(true);
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default function AdOpsDashboardPage() {
|
|||||||
const [filterSite, setFilterSite] = useState('');
|
const [filterSite, setFilterSite] = useState('');
|
||||||
const [filterStatus, setFilterStatus] = useState('');
|
const [filterStatus, setFilterStatus] = useState('');
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default function AdOpsProductsPage() {
|
|||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [form, setForm] = useState({ site: '', product_type: '', name: '', description: '', dimensions: '', duration_days: '', sort_order: '0' });
|
const [form, setForm] = useState({ site: '', product_type: '', name: '', description: '', dimensions: '', duration_days: '', sort_order: '0' });
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ export default function AdminAnalyticsPage() {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const fetchAnalytics = useCallback(async () => {
|
const fetchAnalytics = useCallback(async () => {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export default function AdminArticlesPage() {
|
|||||||
const [newSaving, setNewSaving] = useState(false);
|
const [newSaving, setNewSaving] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const showNotification = (type: 'success' | 'error', message: string) => {
|
const showNotification = (type: 'success' | 'error', message: string) => {
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ export default function AuditLogPage() {
|
|||||||
const PAGE_SIZE = 25;
|
const PAGE_SIZE = 25;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const fetchLogs = useCallback(async () => {
|
const fetchLogs = useCallback(async () => {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export default function BannedTermsPage() {
|
|||||||
}, [searchTerm, filterBanLevel, filterSiteScope, filterActive]);
|
}, [searchTerm, filterBanLevel, filterSiteScope, filterActive]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.push('/login');
|
if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export default function CompanyCoveragePage() {
|
|||||||
}, [activeTab]);
|
}, [activeTab]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.push('/login');
|
if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export default function CompanyTrackerPage() {
|
|||||||
}, [activeTab, queueStatus, queueSiteFilter]);
|
}, [activeTab, queueStatus, queueSiteFilter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.push('/login');
|
if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ export default function ContentStatusPage() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.push('/login');
|
if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export default function EmailAnalyticsDashboard() {
|
|||||||
const [timeRange, setTimeRange] = useState<'3m' | '6m' | '12m'>('6m');
|
const [timeRange, setTimeRange] = useState<'3m' | '6m' | '12m'>('6m');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const fetchData = useCallback(async () => {
|
const fetchData = useCallback(async () => {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ function DomainsContent() {
|
|||||||
const [addForm, setAddForm] = useState({ site: '', domain: '', domain_type: 'subdomain', is_primary: false, sort_order: 0 });
|
const [addForm, setAddForm] = useState({ site: '', domain: '', domain_type: 'subdomain', is_primary: false, sort_order: 0 });
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default function EmailDashboard() {
|
|||||||
const [stats, setStats] = useState<any>(null);
|
const [stats, setStats] = useState<any>(null);
|
||||||
const [loadingData, setLoadingData] = useState(true);
|
const [loadingData, setLoadingData] = useState(true);
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function SendLogPage() {
|
|||||||
const [filterSite, setFilterSite] = useState('');
|
const [filterSite, setFilterSite] = useState('');
|
||||||
const [filterStatus, setFilterStatus] = useState('');
|
const [filterStatus, setFilterStatus] = useState('');
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function SuppressionsPage() {
|
|||||||
const [keywordForm, setKeywordForm] = useState({ keyword: '', keyword_type: 'unsubscribe' });
|
const [keywordForm, setKeywordForm] = useState({ keyword: '', keyword_type: 'unsubscribe' });
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
useEffect(() => { if (!loading && !user) router.push('/login'); }, [user, loading, router]);
|
useEffect(() => { if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname)); }, [user, loading, router]);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoadingData(true);
|
setLoadingData(true);
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export default function ForumDashboardPage() {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.push('/login');
|
if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const fetchStats = useCallback(async () => {
|
const fetchStats = useCallback(async () => {
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ export default function ForumModerationPage() {
|
|||||||
}, [activeTab, threadFilter, replyFilter, page]);
|
}, [activeTab, threadFilter, replyFilter, page]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.push('/login');
|
if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default function WPImportPage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) {
|
if (!loading && !user) {
|
||||||
router.replace('/account');
|
router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}
|
}
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ export default function NewsletterAnalyticsPage() {
|
|||||||
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc');
|
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const fetchData = useCallback(async () => {
|
const fetchData = useCallback(async () => {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export default function CampaignEditorPage() {
|
|||||||
const [previewCharCount, setPreviewCharCount] = useState(0);
|
const [previewCharCount, setPreviewCharCount] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const showNotification = (type: 'success' | 'error', message: string) => {
|
const showNotification = (type: 'success' | 'error', message: string) => {
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default function AdminNewsletterPage() {
|
|||||||
const [appliedTemplate, setAppliedTemplate] = useState<NewsletterTemplate | null>(null);
|
const [appliedTemplate, setAppliedTemplate] = useState<NewsletterTemplate | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
// Handle ?template= query param (from templates page "Use" button)
|
// Handle ?template= query param (from templates page "Use" button)
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default function NewsletterTemplatesPage() {
|
|||||||
const [previewTemplate, setPreviewTemplate] = useState<NewsletterTemplate | null>(null);
|
const [previewTemplate, setPreviewTemplate] = useState<NewsletterTemplate | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const showNotification = (type: 'success' | 'error', message: string) => {
|
const showNotification = (type: 'success' | 'error', message: string) => {
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ export default function NotificationsPage() {
|
|||||||
|
|
||||||
// Auth guard
|
// Auth guard
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user === null) router.push('/login');
|
if (user === null) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, router]);
|
}, [user, router]);
|
||||||
|
|
||||||
// Build notifications from live DB data
|
// Build notifications from live DB data
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ export default function AdminDashboardPage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) {
|
if (!loading && !user) {
|
||||||
router.push('/login');
|
router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}
|
}
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ export default function AdminRolesPage() {
|
|||||||
const [notification, setNotification] = useState<{ type: 'success' | 'error'; message: string } | null>(null);
|
const [notification, setNotification] = useState<{ type: 'success' | 'error'; message: string } | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const showNotification = (type: 'success' | 'error', message: string) => {
|
const showNotification = (type: 'success' | 'error', message: string) => {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default function AdminSuspensionsPage() {
|
|||||||
const [userSearch, setUserSearch] = useState('');
|
const [userSearch, setUserSearch] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const showNotification = (type: 'success' | 'error', message: string) => {
|
const showNotification = (type: 'success' | 'error', message: string) => {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default function AdminUsersPage() {
|
|||||||
const [editSaving, setEditSaving] = useState(false);
|
const [editSaving, setEditSaving] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.replace('/account');
|
if (!loading && !user) router.replace('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
const showNotification = (type: 'success' | 'error' | 'warning', message: string) => {
|
const showNotification = (type: 'success' | 'error' | 'warning', message: string) => {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default function WPUsersAdminPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user) router.push('/login');
|
if (!loading && !user) router.push('/login?next=' + encodeURIComponent(window.location.pathname));
|
||||||
}, [user, loading, router]);
|
}, [user, loading, router]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,7 +1,123 @@
|
|||||||
import { redirect } from "next/navigation";
|
'use client';
|
||||||
|
|
||||||
// /login is an alias for the canonical PR Firm / contributor login at
|
import { Suspense, useState, useEffect } from 'react';
|
||||||
// /client-login. Server-side redirect so any existing bookmarks update.
|
import Link from 'next/link';
|
||||||
export default function LoginPage() {
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
redirect("/client-login");
|
import Header from '@/components/Header';
|
||||||
|
import Footer from '@/components/Footer';
|
||||||
|
import { createClient } from '@/lib/supabase/client';
|
||||||
|
|
||||||
|
function safeNext(next: string | null): string {
|
||||||
|
if (!next) return '/';
|
||||||
|
if (!next.startsWith('/') || next.startsWith('//')) return '/';
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoginInner() {
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useSearchParams();
|
||||||
|
const next = safeNext(params.get('next') || params.get('redirect') || params.get('redirect_to'));
|
||||||
|
const supabase = createClient();
|
||||||
|
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
supabase.auth.getUser().then(({ data: { user } }) => {
|
||||||
|
if (user) router.replace(next);
|
||||||
|
});
|
||||||
|
}, [next, router, supabase]);
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setError(null);
|
||||||
|
if (!email || !password) {
|
||||||
|
setError('Email and password are required.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
const { error: signInError } = await supabase.auth.signInWithPassword({ email, password });
|
||||||
|
setLoading(false);
|
||||||
|
if (signInError) {
|
||||||
|
setError(signInError.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.location.href = next;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
<main className="min-h-screen bg-[#111111]">
|
||||||
|
<div className="max-w-md mx-auto px-4 py-12">
|
||||||
|
<div className="text-center mb-6">
|
||||||
|
<h1 className="text-2xl font-heading font-bold text-white">Sign in to Broadcast Beat</h1>
|
||||||
|
<p className="text-[#888] font-body text-sm mt-1">
|
||||||
|
Editor and admin access. Forum members can also sign in here.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="bg-[#1a1a1a] border border-[#252525] rounded-lg p-6 space-y-4"
|
||||||
|
>
|
||||||
|
{error && (
|
||||||
|
<div className="bg-[#2a0a0a] border border-[#cc0000] text-[#ff8a8a] font-body text-sm px-3 py-2 rounded">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-body uppercase tracking-wider text-[#888] mb-1.5">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
autoComplete="email"
|
||||||
|
required
|
||||||
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#3b82f6]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-body uppercase tracking-wider text-[#888] mb-1.5">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
autoComplete="current-password"
|
||||||
|
required
|
||||||
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#3b82f6]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading}
|
||||||
|
className="w-full bg-[#3b82f6] hover:bg-[#2563eb] disabled:opacity-50 text-white font-body font-semibold py-2 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
{loading ? 'Signing in…' : 'Sign in'}
|
||||||
|
</button>
|
||||||
|
<p className="text-xs text-[#666] font-body text-center pt-2">
|
||||||
|
PR firm / contributor?{' '}
|
||||||
|
<Link href="/client-login" className="text-[#3b82f6] hover:underline">
|
||||||
|
Use the contributor portal
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LoginPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<LoginInner />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { NextResponse, type NextRequest } from "next/server";
|
import { NextResponse, type NextRequest } from "next/server";
|
||||||
|
|
||||||
// PR firms / writers authenticate at the distribute platform, not here.
|
// PR firms / writers authenticate at the distribute platform, not here.
|
||||||
|
// Legacy WordPress URLs still bounce there. /login is now a real BB login
|
||||||
|
// page so the admin backend can use its own session (the BB Supabase cookie
|
||||||
|
// doesn't transfer from distribute's domain).
|
||||||
const AUTH_TARGET = "https://distribution.relevantmediaproperties.com/login";
|
const AUTH_TARGET = "https://distribution.relevantmediaproperties.com/login";
|
||||||
|
|
||||||
const REDIRECT_PATHS = new Set<string>([
|
const REDIRECT_PATHS = new Set<string>([
|
||||||
"/login",
|
|
||||||
"/wp-login",
|
"/wp-login",
|
||||||
"/wp-login.php",
|
"/wp-login.php",
|
||||||
"/wp-admin",
|
"/wp-admin",
|
||||||
"/client-login",
|
"/client-login",
|
||||||
"/register",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ─── Server-side impression beacon (AdSanity-equivalent) ────────────────────
|
// ─── Server-side impression beacon (AdSanity-equivalent) ────────────────────
|
||||||
@@ -118,12 +119,10 @@ export async function middleware(req: NextRequest) {
|
|||||||
export const config = {
|
export const config = {
|
||||||
matcher: [
|
matcher: [
|
||||||
"/((?!api/|_next/|legacy/|.*\\.).*)",
|
"/((?!api/|_next/|legacy/|.*\\.).*)",
|
||||||
"/login",
|
|
||||||
"/wp-login",
|
"/wp-login",
|
||||||
"/wp-login.php",
|
"/wp-login.php",
|
||||||
"/wp-admin/:path*",
|
"/wp-admin/:path*",
|
||||||
"/wp-login/:path*",
|
"/wp-login/:path*",
|
||||||
"/client-login",
|
"/client-login",
|
||||||
"/register",
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user