This commit is contained in:
“kirukib” 2026-02-22 22:45:45 +03:00
parent 4efcacaeba
commit 94064e66f7
8 changed files with 11776 additions and 5 deletions

View File

@ -1,4 +1,5 @@
import { View, ScrollView } from 'react-native'; import { View, ScrollView, Pressable } from 'react-native';
import { router } from 'expo-router';
import { Text } from '@/components/ui/text'; import { Text } from '@/components/ui/text';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@ -28,10 +29,10 @@ export default function ProfileScreen() {
<Text className="text-muted-foreground">Language</Text> <Text className="text-muted-foreground">Language</Text>
<Text className="text-gray-900">English</Text> <Text className="text-gray-900">English</Text>
</View> </View>
<View className="flex-row justify-between py-2"> <Pressable onPress={() => router.push('/notifications')} className="flex-row justify-between py-2">
<Text className="text-muted-foreground">Notifications</Text> <Text className="text-muted-foreground">Notifications</Text>
<Text className="text-gray-900">On</Text> <Text className="text-primary font-medium">Manage</Text>
</View> </Pressable>
</CardContent> </CardContent>
</Card> </Card>
@ -46,6 +47,9 @@ export default function ProfileScreen() {
</CardContent> </CardContent>
</Card> </Card>
<Button variant="outline" className="mt-2" onPress={() => router.push('/login')}>
<Text className="font-medium">Sign in (different account)</Text>
</Button>
<Button variant="destructive" className="mt-2"> <Button variant="destructive" className="mt-2">
<Text className="font-medium">Log out</Text> <Text className="font-medium">Log out</Text>
</Button> </Button>

View File

@ -12,7 +12,20 @@ export default function RootLayout() {
<SafeAreaProvider> <SafeAreaProvider>
<View className="flex-1 bg-background"> <View className="flex-1 bg-background">
<StatusBar style="light" /> <StatusBar style="light" />
<Stack screenOptions={{ headerShown: false }} /> <Stack
screenOptions={{
headerStyle: { backgroundColor: '#2d2d2d' },
headerTintColor: '#ffffff',
headerTitleStyle: { fontWeight: '600' },
}}
>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="proforma/[id]" options={{ title: 'Proforma request' }} />
<Stack.Screen name="payments/[id]" options={{ title: 'Payment' }} />
<Stack.Screen name="notifications/index" options={{ title: 'Notifications' }} />
<Stack.Screen name="notifications/settings" options={{ title: 'Notification settings' }} />
<Stack.Screen name="login" options={{ title: 'Sign in', headerShown: false }} />
</Stack>
<PortalHost /> <PortalHost />
</View> </View>
</SafeAreaProvider> </SafeAreaProvider>

30
app/login.tsx Normal file
View File

@ -0,0 +1,30 @@
import { View, ScrollView } from 'react-native';
import { router } from 'expo-router';
import { Text } from '@/components/ui/text';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
export default function LoginScreen() {
return (
<ScrollView className="flex-1 bg-background" contentContainerStyle={{ padding: 16, paddingVertical: 48 }}>
<Text className="mb-6 text-center text-2xl font-bold text-gray-900">Yaltopia Tickets</Text>
<Card className="mb-4">
<CardHeader>
<CardTitle>Sign in</CardTitle>
<CardDescription>Use the same account as the web app.</CardDescription>
</CardHeader>
<CardContent className="gap-3">
<Button className="bg-primary">
<Text className="text-primary-foreground font-medium">Email & password</Text>
</Button>
<Button variant="outline">
<Text className="font-medium">Continue with Google</Text>
</Button>
</CardContent>
</Card>
<Button variant="ghost" onPress={() => router.back()}>
<Text className="font-medium">Back</Text>
</Button>
</ScrollView>
);
}

View File

@ -0,0 +1,33 @@
import { View, ScrollView, Pressable } from 'react-native';
import { router } from 'expo-router';
import { Text } from '@/components/ui/text';
import { Card, CardContent } from '@/components/ui/card';
const MOCK_NOTIFICATIONS = [
{ id: '1', title: 'Invoice reminder', body: 'Invoice #2 to Robin Murray is due in 2 days.', time: '2h ago', read: false },
{ id: '2', title: 'Payment received', body: 'Payment of $500 received for Invoice #4.', time: '1d ago', read: true },
{ id: '3', title: 'Proforma submission', body: 'Vendor A submitted a quote for Marketing Landing Page.', time: '2d ago', read: true },
];
export default function NotificationsScreen() {
return (
<ScrollView className="flex-1 bg-background" contentContainerStyle={{ padding: 16, paddingBottom: 32 }}>
<View className="mb-4 flex-row items-center justify-between">
<Text className="text-xl font-semibold text-gray-900">Notifications</Text>
<Pressable onPress={() => router.push('/notifications/settings')}>
<Text className="text-primary font-medium">Settings</Text>
</Pressable>
</View>
{MOCK_NOTIFICATIONS.map((n) => (
<Card key={n.id} className={`mb-2 ${!n.read ? 'border-primary/30' : ''}`}>
<CardContent className="py-3">
<Text className="font-semibold text-gray-900">{n.title}</Text>
<Text className="text-muted-foreground mt-1 text-sm">{n.body}</Text>
<Text className="text-muted-foreground mt-1 text-xs">{n.time}</Text>
</CardContent>
</Card>
))}
</ScrollView>
);
}

View File

@ -0,0 +1,41 @@
import { View, ScrollView, Switch } from 'react-native';
import { router } from 'expo-router';
import { useState } from 'react';
import { Text } from '@/components/ui/text';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
export default function NotificationSettingsScreen() {
const [invoiceReminders, setInvoiceReminders] = useState(true);
const [daysBeforeDue, setDaysBeforeDue] = useState(2);
const [newsAlerts, setNewsAlerts] = useState(true);
const [reportReady, setReportReady] = useState(true);
return (
<ScrollView className="flex-1 bg-background" contentContainerStyle={{ padding: 16, paddingBottom: 32 }}>
<Card className="mb-4">
<CardHeader>
<CardTitle>Notification settings</CardTitle>
</CardHeader>
<CardContent className="gap-4">
<View className="flex-row items-center justify-between">
<Text className="text-gray-900">Invoice reminders</Text>
<Switch value={invoiceReminders} onValueChange={setInvoiceReminders} />
</View>
<View className="flex-row items-center justify-between">
<Text className="text-gray-900">News & announcements</Text>
<Switch value={newsAlerts} onValueChange={setNewsAlerts} />
</View>
<View className="flex-row items-center justify-between">
<Text className="text-gray-900">Report ready</Text>
<Switch value={reportReady} onValueChange={setReportReady} />
</View>
</CardContent>
</Card>
<Button variant="outline" onPress={() => router.back()}>
<Text className="font-medium">Back</Text>
</Button>
</ScrollView>
);
}

44
app/payments/[id].tsx Normal file
View File

@ -0,0 +1,44 @@
import { View, ScrollView } from 'react-native';
import { useLocalSearchParams, router } from 'expo-router';
import { Text } from '@/components/ui/text';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
export default function PaymentDetailScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
return (
<ScrollView className="flex-1 bg-background" contentContainerStyle={{ padding: 16, paddingBottom: 32 }}>
<Card className="mb-4">
<CardHeader>
<CardTitle>Payment #{id ?? '—'}</CardTitle>
</CardHeader>
<CardContent className="gap-2">
<View className="flex-row justify-between py-2">
<Text className="text-muted-foreground">Amount</Text>
<Text className="font-semibold text-gray-900">$2,000.00</Text>
</View>
<View className="flex-row justify-between py-2">
<Text className="text-muted-foreground">Source</Text>
<Text className="text-gray-900">Telebirr</Text>
</View>
<View className="flex-row justify-between py-2">
<Text className="text-muted-foreground">Date</Text>
<Text className="text-gray-900">Sep 11, 2022</Text>
</View>
<View className="flex-row justify-between py-2">
<Text className="text-muted-foreground">Associated invoice</Text>
<Text className="text-amber-600">Not linked</Text>
</View>
</CardContent>
</Card>
<Button className="mb-3 bg-primary" onPress={() => {}}>
<Text className="text-primary-foreground font-medium">Associate to invoice</Text>
</Button>
<Button variant="outline" onPress={() => router.back()}>
<Text className="font-medium">Back to payments</Text>
</Button>
</ScrollView>
);
}

66
app/proforma/[id].tsx Normal file
View File

@ -0,0 +1,66 @@
import { View, ScrollView } from 'react-native';
import { useLocalSearchParams, router } from 'expo-router';
import { Text } from '@/components/ui/text';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
const MOCK_ITEMS = [
{ description: 'Marketing Landing Page Package', qty: 1, unitPrice: 1000, total: 1000 },
{ description: 'Instagram Post Initial Design', qty: 4, unitPrice: 100, total: 400 },
];
const MOCK_SUBTOTAL = 1400;
const MOCK_TAX = 140;
const MOCK_TOTAL = 1540;
export default function ProformaDetailScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
return (
<ScrollView className="flex-1 bg-background" contentContainerStyle={{ padding: 16, paddingBottom: 32 }}>
<Card className="mb-4">
<CardHeader>
<CardTitle>Proforma Request #{id ?? '—'}</CardTitle>
<CardDescription>Marketing Landing Page Package</CardDescription>
<Text className="text-muted-foreground mt-1 text-sm">Deadline: Sep 20, 2022 · OPEN</Text>
</CardHeader>
<CardContent className="gap-2">
{MOCK_ITEMS.map((item, i) => (
<View key={i} className="flex-row justify-between py-2">
<Text className="text-gray-700">{item.description} × {item.qty}</Text>
<Text className="font-medium text-gray-900">${item.total.toLocaleString()}</Text>
</View>
))}
<View className="mt-2 border-t border-border pt-2">
<View className="flex-row justify-between">
<Text className="text-muted-foreground">Subtotal</Text>
<Text className="text-gray-900">${MOCK_SUBTOTAL.toLocaleString()}</Text>
</View>
<View className="flex-row justify-between">
<Text className="text-muted-foreground">Tax (10%)</Text>
<Text className="text-gray-900">${MOCK_TAX.toLocaleString()}</Text>
</View>
<View className="flex-row justify-between">
<Text className="font-semibold text-gray-900">Total</Text>
<Text className="font-semibold text-gray-900">${MOCK_TOTAL.toLocaleString()}</Text>
</View>
</View>
</CardContent>
</Card>
<Button className="mb-3 bg-primary" onPress={() => {}}>
<Text className="text-primary-foreground font-medium">Send to contacts</Text>
</Button>
<Button variant="outline" onPress={() => router.back()}>
<Text className="font-medium">Back to list</Text>
</Button>
<Text className="text-muted-foreground mt-6 mb-2 text-sm">Submissions (mock)</Text>
<Card>
<CardContent className="py-3">
<Text className="font-medium text-gray-900">Vendor A $1,450</Text>
<Text className="text-muted-foreground text-sm">Submitted Sep 15, 2022</Text>
</CardContent>
</Card>
</ScrollView>
);
}

11540
swagger.json Normal file

File diff suppressed because it is too large Load Diff