Skip to content
112 changes: 101 additions & 11 deletions src/app/admin/settings/CalendarIntegrations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import type { CalendarItem } from '@/app/admin/settings/components/CalendarForm'
import CalendarOptionsList from '@/app/admin/settings/components/CalendarForm';
import SectionDivider from '@/app/admin/settings/components/SectionDivider';
import SectionHeader from '@/app/admin/settings/components/SectionHeader';
import ProFeatureModal from '@/components/ui/ProFeatureModal';
import { useAppSelector } from '@/redux/hooks';
import theme from '@/theme';

const InfoRow = styled(Box)({
Expand Down Expand Up @@ -45,15 +47,18 @@ const IconAndContentRow = styled(Box)({
gap: theme.spacing(1.5),
});

const ConnectButton = styled(Button)({
backgroundColor: '#000000',
const ConnectButton = styled(Button, {
shouldForwardProp: prop => prop !== '$disabled',
})<{ $disabled?: boolean }>(({ $disabled }) => ({
backgroundColor: $disabled ? '#e0e0e0' : '#000000',
color: 'white',
padding: `${theme.spacing(1)} ${theme.spacing(2)}`,
textTransform: 'none',
boxShadow: 'none',
'&:hover': {
backgroundColor: '#374151',
backgroundColor: $disabled ? '#e0e0e0' : '#374151',
},
});
}));

const RemoveButton = styled(Button)({
backgroundColor: 'transparent',
Expand Down Expand Up @@ -87,7 +92,18 @@ const CustomCheckbox = styled(Checkbox)({
},
});

export default function IntegrationsSection() {
interface IntegrationsSectionProps {
editable?: boolean;
showProBadge?: boolean;
user?: { email?: string };
}

export default function IntegrationsSection({
editable = false,
showProBadge = false,
}: IntegrationsSectionProps) {
const user = useAppSelector(state => state.auth.user);
const [showProModal, setShowProModal] = useState(false);
const [isConnected, setIsConnected] = useState(false);
const [showGoogleEvents, setShowGoogleEvents] = useState(true);
const [calendars, setCalendars] = useState<CalendarItem[]>([
Expand All @@ -107,12 +123,21 @@ export default function IntegrationsSection() {
},
]);

const handleUnlockPro = () => setShowProModal(true);
const handleCloseProModal = () => setShowProModal(false);
const handleUpgrade = () => {
// Redirect to billing or upgrade page
window.location.href = '/admin/billing';
};

const handleConnect = () => {
if (!editable) return;
// TODO: Implement Google Calendar OAuth connection
setIsConnected(true);
};

const handleRemove = () => {
if (!editable) return;
setIsConnected(false);
setShowGoogleEvents(true);
setCalendars(prev =>
Expand All @@ -124,6 +149,7 @@ export default function IntegrationsSection() {
};

const handleCalendarToggle = (calendarId: string) => {
if (!editable) return;
setCalendars(prev =>
prev.map(cal =>
cal.id === calendarId ? { ...cal, checked: !cal.checked } : cal,
Expand All @@ -134,7 +160,29 @@ export default function IntegrationsSection() {
return (
<>
<SectionDivider />
<SectionHeader title="Integrations" />
<Box display="flex" alignItems="center" gap={1} mb={1}>
<SectionHeader title="Integrations" />
{showProBadge && !editable && (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 0.5,
backgroundColor: '#fff2d0',
padding: '2px 6px',
borderRadius: '8px',
fontSize: '10px',
fontWeight: 'bold',
color: '#333',
border: '1px solid #ffd700',
mb: '20px',
}}
>
<Image src="/plan/pro.svg" alt="Pro" width={12} height={12} />
<span style={{ fontSize: '10px', fontWeight: 'bold' }}>PRO</span>
</Box>
)}
</Box>
<InfoRow>
<IntegrationItem>
<LeftSection>
Expand All @@ -151,7 +199,7 @@ export default function IntegrationsSection() {
/>
<ContentSection>
<Typography variant="body2" color="text.primary" sx={{ mb: 1 }}>
[email protected]
{user?.email ?? ''}
</Typography>
<Typography variant="body2" color="text.secondary">
Sync your appointments to Google Calendar. Online booking
Expand All @@ -163,9 +211,39 @@ export default function IntegrationsSection() {
</LeftSection>

{isConnected ? (
<RemoveButton onClick={handleRemove}>Remove</RemoveButton>
<RemoveButton onClick={handleRemove} disabled={!editable}>
Remove
</RemoveButton>
) : editable ? (
<ConnectButton
onClick={handleConnect}
disabled={!editable}
$disabled={!editable}
>
Connect
</ConnectButton>
) : (
<ConnectButton onClick={handleConnect}>Connect</ConnectButton>
<Button
variant="contained"
color="warning"
onClick={handleUnlockPro}
sx={{
backgroundColor: '#fff2d0',
color: '#333',
fontWeight: 600,
textTransform: 'none',
borderRadius: 2,
boxShadow: 'none',
'&:hover': {
backgroundColor: '#ffd54f',
},
}}
startIcon={
<Image src="/plan/pro.svg" alt="Pro" width={16} height={16} />
}
>
Unlock with Pro
</Button>
)}
</IntegrationItem>

Expand All @@ -175,15 +253,18 @@ export default function IntegrationsSection() {
Connected account:
</Typography>
<Typography variant="body2" color="text.primary" sx={{ mb: 2 }}>
[email protected]
{user?.email ?? ''}
</Typography>

<FormControlLabel
control={
<CustomCheckbox
checked={showGoogleEvents}
onChange={e => setShowGoogleEvents(e.target.checked)}
onChange={e => {
if (editable) setShowGoogleEvents(e.target.checked);
}}
size="small"
disabled={!editable}
/>
}
label={
Expand All @@ -202,10 +283,19 @@ export default function IntegrationsSection() {
<CalendarOptionsList
calendars={calendars}
onToggle={handleCalendarToggle}
editable={editable}
/>
</ConnectedInfo>
)}
</InfoRow>

{/* Render the modal here */}
<ProFeatureModal
open={showProModal}
onClose={handleCloseProModal}
onUpgrade={handleUpgrade}
featureName="Calendar Integrations"
/>
</>
);
}
110 changes: 84 additions & 26 deletions src/app/admin/settings/CompanyInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
'use client';
import React from 'react';
import { Box, Button } from '@mui/material';
import Image from 'next/image';
import React, { useState } from 'react';

import EditableSection from '@/app/admin/settings/components/EditableSection';
import SectionHeader from '@/app/admin/settings/components/SectionHeader';
import ProFeatureModal from '@/components/ui/ProFeatureModal';
import {
useCheckABNExistsMutation,
useGetCompanyInfoQuery,
Expand Down Expand Up @@ -62,9 +66,19 @@ const validateABNFormat = (abn: string): ValidationResult => {
return { isValid: true };
};

export default function CompanyInfoSection() {
export default function CompanyInfoSection({
editable = false,
showProBadge = false,
}: { editable?: boolean; showProBadge?: boolean } = {}) {
const user = useAppSelector(state => state.auth.user);
const [checkABNExists] = useCheckABNExistsMutation();
const [showProModal, setShowProModal] = useState(false);
const handleUnlockPro = () => setShowProModal(true);
const handleCloseProModal = () => setShowProModal(false);
const handleUpgrade = () => {
window.location.href = '/admin/billing';
};
const [forceEdit, setForceEdit] = useState(false);

// Synchronous validation for real-time feedback (format only)
const validateABN = (abn: string): ValidationResult => {
Expand Down Expand Up @@ -118,31 +132,75 @@ export default function CompanyInfoSection() {
abn: companyData.abn,
}
: undefined;

// Handler for edit button
const handleEditClick = () => {
if (!editable) {
setShowProModal(true);
} else {
setForceEdit(true);
}
};

const titleWithBadge = (
<Box display="flex" alignItems="center" gap={1}>
<SectionHeader title="Company Info" />
{showProBadge && !editable && (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 0.5,
backgroundColor: '#fff2d0',
padding: '2px 6px',
borderRadius: '8px',
fontSize: '10px',
fontWeight: 'bold',
color: '#333',
border: '1px solid #ffd700',
mb: '20px',
}}
>
<Image src="/plan/pro.svg" alt="Pro" width={12} height={12} />
<span style={{ fontSize: '10px', fontWeight: 'bold' }}>PRO</span>
</Box>
)}
</Box>
);

return (
<EditableSection
title="Company Info"
fields={[
{
label: 'Company Name:',
key: 'companyName',
placeholder: 'e.g. Google',
validate: validateCompanyName,
},
{
label: 'ABN:',
key: 'abn',
placeholder: 'e.g. 12345678909',
validate: validateABN,
},
]}
data={convertedData}
isLoading={isLoading}
onSave={handleSave}
initialValues={{
companyName: '',
abn: '',
}}
/>
<>
<EditableSection
title={titleWithBadge}
fields={[
{
label: 'Company Name:',
key: 'companyName',
placeholder: 'e.g. Google',
validate: validateCompanyName,
},
{
label: 'ABN:',
key: 'abn',
placeholder: 'e.g. 12345678909',
validate: validateABN,
},
]}
data={convertedData}
isLoading={isLoading}
onSave={handleSave}
initialValues={{
companyName: '',
abn: '',
}}
{...(!editable && { onEdit: handleEditClick })}
/>
<ProFeatureModal
open={showProModal}
onClose={handleCloseProModal}
onUpgrade={handleUpgrade}
featureName="Company Information"
/>
</>
);
}
9 changes: 7 additions & 2 deletions src/app/admin/settings/SettingsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import CompanyInfoSection from '@/app/admin/settings/CompanyInfo';
import GreetingSection from '@/app/admin/settings/GreetingSection';
import UserProfileSection from '@/app/admin/settings/UserProfileSection';
import VerificationSection from '@/app/admin/settings/VerificationSection';
import { useSubscription } from '@/features/subscription/useSubscription';
import theme from '@/theme';
import { getPlanTier, isProPlan } from '@/utils/planUtils';

const SettingsContainer = styled(Box)({
padding: theme.spacing(3, 4),
Expand All @@ -34,13 +36,16 @@ const SettingsContainer = styled(Box)({
});

export default function SettingsSection() {
const { subscription } = useSubscription();
const planTier = getPlanTier(subscription);
const isPro = isProPlan(planTier);
return (
<SettingsContainer>
<GreetingSection />
<UserProfileSection />
<VerificationSection />
<IntegrationsSection />
<CompanyInfoSection />
<IntegrationsSection editable={isPro} showProBadge />
<CompanyInfoSection editable={isPro} showProBadge />
<BillingAddressSection />
</SettingsContainer>
);
Expand Down
5 changes: 4 additions & 1 deletion src/app/admin/settings/components/CalendarForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,14 @@ interface CalendarOptionsListProps {
calendars: CalendarItem[];
onToggle: (calendarId: string) => void;
title?: string;
editable?: boolean;
}

export default function CalendarOptionsList({
calendars,
onToggle,
title = 'Calendars to show:',
editable = false,
}: CalendarOptionsListProps) {
return (
<>
Expand All @@ -105,8 +107,9 @@ export default function CalendarOptionsList({
</CalendarLabel>
<CustomSwitch
checked={calendar.checked}
onChange={() => onToggle(calendar.id)}
onChange={() => editable && onToggle(calendar.id)}
size="small"
disabled={!editable}
/>
</CalendarOption>
))}
Expand Down
Loading
Loading