305 lines
10 KiB
TypeScript
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,
|
|
},
|
|
});
|