All files / components/layout Footer.tsx

100% Statements 10/10
100% Branches 2/2
100% Functions 5/5
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128                    2x         2x     2x                       2x                 2x 18x 16x                       14x                                                               56x                                           112x                                        
// src/components/layout/Footer.tsx
import type {ReactElement} from 'react';
import {Link as RouterLink} from 'react-router-dom';
import {Box, Container, Divider, Link, Stack, Typography} from '@mui/material';
import {LEGAL_DOC_LIST} from '../../content/legal';
 
// ============================================================================
// Constants
// ============================================================================
 
const SUPPORT_EMAIL = 'admin@flowstatecapital.ai';
 
// Calendar year for the copyright line. Computed once at module load — the
// value only needs to be correct to the year, so a render-time Date is overkill
// and would churn on every paint.
const COPYRIGHT_YEAR = new Date().getFullYear();
 
const COMPLIANCE_TEXT =
	'FlowState is a financial technology platform and is not a bank or registered investment adviser. ' +
	'Any credit product is subject to onboarding, KYB/KYC, underwriting, collateral eligibility, ' +
	'documentation, ongoing compliance, and approval. Services may not be available in all jurisdictions. ' +
	'Nothing herein constitutes legal, tax, investment, or accounting advice, or an offer, solicitation, ' +
	'or recommendation to buy or sell any security or financial instrument.';
 
interface FooterLink {
	label: string;
	to?: string;
	href?: string;
}
 
const FOOTER_LINKS: FooterLink[] = [
	{label: 'Privacy Policy', to: '/privacy'},
	{label: 'Terms & Conditions', to: '/terms'},
	{label: 'FAQ', to: '/faq'},
	{label: 'Support', href: `mailto:${SUPPORT_EMAIL}`}
];
 
// Secondary row of policy/disclosure pages (generated content, FSC-80/90/91
// cluster). Privacy lives in the primary row above, so it's excluded here.
const POLICY_LINKS: {label: string; to: string}[] = LEGAL_DOC_LIST
	.filter((doc) => doc.slug !== 'privacy')
	.map((doc) => ({label: doc.title, to: `/${doc.slug}`}));
 
// ============================================================================
// Footer Component
// ============================================================================
 
/**
 * Site-wide footer (FSC-62 / FSC-80 / FSC-91). Renders the legal/support link
 * row, a copyright line, and the standing compliance disclaimer. Used inside
 * the authenticated app shell and on public legal pages.
 */
export default function Footer(): ReactElement {
	return (
		<Box
			component="footer"
			sx={{
				mt: 6,
				pt: 3,
				pb: 4,
				borderTop: 1,
				borderColor: 'divider'
			}}
		>
			<Container maxWidth="lg" disableGutters>
				<Stack
					direction={{xs: 'column', sm: 'row'}}
					spacing={{xs: 1.5, sm: 2}}
					sx={{
						alignItems: {xs: 'flex-start', sm: 'center'},
						justifyContent: 'space-between',
						mb: 2
					}}
				>
					<Typography variant="body2" sx={{color: 'text.secondary'}}>
						© {COPYRIGHT_YEAR} FlowState Capital. All rights reserved.
					</Typography>
 
					<Stack
						direction="row"
						spacing={2}
						sx={{flexWrap: 'wrap', rowGap: 1}}
						divider={<Divider orientation="vertical" flexItem/>}
					>
						{FOOTER_LINKS.map((link) => (
							<Link
								key={link.label}
								component={link.to !== undefined ? RouterLink : 'a'}
								to={link.to}
								href={link.href}
								underline="hover"
								variant="body2"
								sx={{color: 'text.secondary', '&:hover': {color: 'primary.main'}}}
							>
								{link.label}
							</Link>
						))}
					</Stack>
				</Stack>
 
				<Stack
					direction="row"
					spacing={1.5}
					sx={{flexWrap: 'wrap', rowGap: 0.5, mb: 2}}
					divider={<Divider orientation="vertical" flexItem/>}
				>
					{POLICY_LINKS.map((link) => (
						<Link
							key={link.label}
							component={RouterLink}
							to={link.to}
							underline="hover"
							variant="caption"
							sx={{color: 'text.disabled', '&:hover': {color: 'primary.main'}}}
						>
							{link.label}
						</Link>
					))}
				</Stack>
 
				<Typography variant="caption" sx={{color: 'text.disabled', display: 'block', lineHeight: 1.6}}>
					{COMPLIANCE_TEXT}
				</Typography>
			</Container>
		</Box>
	);
}