Files
ProcureLinc-Mobile-App/src/Component/TermsAndConditionsModal/TermsAndConditionsModal.tsx
T

305 lines
10 KiB
TypeScript

import React, { ReactNode, useMemo } from 'react';
import { View, Modal, ScrollView, TouchableOpacity, StyleSheet } from 'react-native';
import { CheckCircle, X, FileText, Shield, Users, Brain, Lock, RefreshCw, Scale } from 'lucide-react-native';
import Animated, { FadeInUp, FadeInDown, FadeIn } from 'react-native-reanimated';
import { useTheme } from '../../Hooks/useTheme';
import AppText from '../../components/AppText';
type ModalProps = {
visible: boolean;
onClose: () => void;
onChecked: () => void;
};
const TermsAndConditionsModal: React.FC<ModalProps> = ({ visible, onClose, onChecked }) => {
const { colors, spacing, shadows, typography } = useTheme();
const styles = useMemo(() => makeStyles(colors, spacing, shadows, typography), [colors, spacing, shadows, typography]);
return (
<Modal
animationType="slide"
transparent={false}
visible={visible}
onRequestClose={onClose}
>
<View style={styles.container}>
{/* ═══ Header ═══ */}
<Animated.View
entering={FadeInDown.duration(400).springify()}
style={styles.header}
>
<View style={styles.headerRow}>
<View style={{ flex: 1 }}>
<AppText variant="h3" color={colors.buttonText} style={{ fontWeight: '800' }}>
Terms & Conditions
</AppText>
<AppText variant="caption" color={`${colors.buttonText}CC`} style={{ marginTop: 4 }}>
Last updated: {new Date().toLocaleDateString()}
</AppText>
</View>
<TouchableOpacity
onPress={onClose}
style={styles.closeButton}
activeOpacity={0.7}
>
<X size={20} color={colors.buttonText} />
</TouchableOpacity>
</View>
</Animated.View>
{/* ═══ Content ═══ */}
<ScrollView
contentContainerStyle={styles.scrollContainer}
showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps="handled"
>
<Section
title="1. Acceptance of Terms"
icon={<FileText size={18} color={colors.primary} />}
styles={styles}
colors={colors}
spacing={spacing}
index={0}
>
<AppText variant="bodySmall" color={colors.textSecondary} style={styles.paragraph}>
By accessing or using our services, you agree to be bound by these Terms and Conditions.
If you do not agree with any part of these terms, you must not use our services.
</AppText>
</Section>
<Section
title="2. User Responsibilities"
icon={<Users size={18} color={colors.primary} />}
styles={styles}
colors={colors}
spacing={spacing}
index={1}
>
<AppText variant="bodySmall" color={colors.textSecondary} style={styles.paragraph}>
You are responsible for maintaining the confidentiality of your account information and
for all activities that occur under your account. You agree to notify us immediately
of any unauthorized use of your account.
</AppText>
</Section>
<Section
title="3. Intellectual Property"
icon={<Brain size={18} color={colors.primary} />}
styles={styles}
colors={colors}
spacing={spacing}
index={2}
>
<AppText variant="bodySmall" color={colors.textSecondary} style={styles.paragraph}>
All content included on our platform, such as text, graphics, logos, and software, is
the property of our company and protected by intellectual property laws. You may not
reproduce, distribute, or create derivative works without our express permission.
</AppText>
</Section>
<Section
title="4. Limitation of Liability"
icon={<Shield size={18} color={colors.primary} />}
styles={styles}
colors={colors}
spacing={spacing}
index={3}
>
<AppText variant="bodySmall" color={colors.textSecondary} style={styles.paragraph}>
Our company shall not be liable for any indirect, incidental, special, or consequential
damages resulting from the use or inability to use our services, even if we have been
advised of the possibility of such damages.
</AppText>
</Section>
<Section
title="5. Privacy Policy"
icon={<Lock size={18} color={colors.primary} />}
styles={styles}
colors={colors}
spacing={spacing}
index={4}
>
<AppText variant="bodySmall" color={colors.textSecondary} style={styles.paragraph}>
Your use of our services is also governed by our Privacy Policy, which explains how we
collect, use, and protect your personal information. Please review our Privacy Policy
to understand our practices.
</AppText>
</Section>
<Section
title="6. Changes to Terms"
icon={<RefreshCw size={18} color={colors.primary} />}
styles={styles}
colors={colors}
spacing={spacing}
index={5}
>
<AppText variant="bodySmall" color={colors.textSecondary} style={styles.paragraph}>
We reserve the right to modify these terms at any time. We will notify you of any changes
by posting the new Terms and Conditions on this page. Your continued use of the service
after such modifications constitutes your acceptance of the new terms.
</AppText>
</Section>
<Section
title="7. Governing Law"
icon={<Scale size={18} color={colors.primary} />}
styles={styles}
colors={colors}
spacing={spacing}
index={6}
>
<AppText variant="bodySmall" color={colors.textSecondary} style={styles.paragraph}>
These Terms shall be governed by and construed in accordance with the laws of the
jurisdiction in which our company is registered, without regard to its conflict of law provisions.
</AppText>
</Section>
<Animated.View entering={FadeInUp.delay(500).springify()}>
<AppText variant="caption" color={colors.textSecondary} style={styles.footerText}>
If you have any questions about these Terms, please contact us at legal@company.com.
</AppText>
</Animated.View>
{/* Spacer for button */}
<View style={{ height: 100 }} />
</ScrollView>
{/* ═══ Accept Button ═══ */}
<Animated.View
entering={FadeInDown.delay(300).springify()}
style={styles.acceptButtonWrapper}
>
<TouchableOpacity
style={styles.acceptButton}
onPress={() => {
onClose();
onChecked();
}}
activeOpacity={0.85}
>
<CheckCircle color={colors.buttonText} size={22} strokeWidth={2.5} />
<AppText variant="button" color={colors.buttonText} style={{ marginLeft: 10 }}>
I Accept
</AppText>
</TouchableOpacity>
</Animated.View>
</View>
</Modal>
);
};
// ── Section sub-component ─────────────────────────────────────────────
type SectionProps = {
title: string;
icon: ReactNode;
children: ReactNode;
styles: any;
colors: any;
spacing: any;
index: number;
};
const Section: React.FC<SectionProps> = ({ title, icon, children, styles, colors, spacing, index }) => (
<Animated.View
entering={FadeInUp.delay(100 + index * 60).springify()}
style={styles.section}
>
<View style={styles.sectionHeader}>
{icon}
<AppText variant="bodySmall" color={colors.text} style={styles.sectionTitle}>
{title}
</AppText>
</View>
{children}
</Animated.View>
);
export default TermsAndConditionsModal;
// ── Styles ────────────────────────────────────────────────────────────
const makeStyles = (colors: any, spacing: any, shadows: any, typography: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
header: {
backgroundColor: colors.primary,
paddingTop: spacing.xl,
paddingBottom: spacing.l,
paddingHorizontal: spacing.l,
borderBottomLeftRadius: 20,
borderBottomRightRadius: 20,
},
headerRow: {
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-between',
},
closeButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: 'rgba(255,255,255,0.2)',
justifyContent: 'center',
alignItems: 'center',
},
scrollContainer: {
padding: spacing.l,
paddingBottom: 40,
},
section: {
backgroundColor: colors.surface,
borderRadius: 14,
padding: spacing.m,
marginBottom: spacing.m,
borderWidth: 1,
borderColor: colors.border,
},
sectionHeader: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
marginBottom: spacing.s,
paddingBottom: spacing.s,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.border,
},
sectionTitle: {
fontWeight: '700',
fontSize: typography.size.s,
},
paragraph: {
lineHeight: 22,
},
footerText: {
fontStyle: 'italic',
textAlign: 'center',
lineHeight: 20,
marginTop: spacing.m,
},
acceptButtonWrapper: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
padding: spacing.m,
paddingBottom: spacing.xl,
backgroundColor: colors.surface,
borderTopWidth: 1,
borderTopColor: colors.border,
},
acceptButton: {
backgroundColor: colors.primary,
borderRadius: 14,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 16,
},
});