refactor: update component exports to named exports and enhance UI structure

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-03-30 19:39:59 -03:00
parent 661b821f37
commit e8cd469ee4
40 changed files with 325 additions and 401 deletions

View file

@ -1,11 +0,0 @@
# Tauri + React + Typescript
This template should help get you started developing with Tauri, React and Typescript in Vite.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
upx --best --lzma /home/rodriguez/Sources/my-tauri-app/src-tauri/target/release/my-tauri-app

View file

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View file

Before

Width:  |  Height:  |  Size: 974 B

After

Width:  |  Height:  |  Size: 974 B

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

Before

Width:  |  Height:  |  Size: 903 B

After

Width:  |  Height:  |  Size: 903 B

View file

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,10 +1,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import DateTimePicker from '@react-native-community/datetimepicker';
import { Picker } from '@react-native-picker/picker';
const accountFormSchema = z.object({ const accountFormSchema = z.object({
name: z name: z
@ -55,28 +52,23 @@ export function AccountForm() {
}; };
return ( return (
<View> <div className="space-y-4">
<Controller <Controller
control={control} control={control}
name="name" name="name"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Name</Text> <label className="block text-sm font-medium mb-1">Name</label>
<TextInput <input
style={{ className="w-full p-2 border rounded"
borderWidth: 1, onChange={(e) => onChange(e.target.value)}
borderColor: '#ccc',
borderRadius: 4,
padding: 8,
}}
onChangeText={onChange}
value={value} value={value}
placeholder="Your name" placeholder="Your name"
/> />
<Text style={{ fontSize: 14, color: '#666', marginTop: 4 }}> <p className="text-sm text-gray-500 mt-1">
This is the name that will be displayed on your profile and in emails. This is the name that will be displayed on your profile and in emails.
</Text> </p>
</View> </div>
)} )}
/> />
@ -84,36 +76,32 @@ export function AccountForm() {
control={control} control={control}
name="dob" name="dob"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Date of birth</Text> <label className="block text-sm font-medium mb-1">Date of birth</label>
<TouchableOpacity <button
style={{ type="button"
borderWidth: 1, className="w-full p-2 border rounded text-left"
borderColor: '#ccc', onClick={() => setShowDatePicker(true)}
borderRadius: 4,
padding: 8,
}}
onPress={() => setShowDatePicker(true)}
> >
<Text>{value.toDateString()}</Text> {value.toDateString()}
</TouchableOpacity> </button>
{showDatePicker && ( {showDatePicker && (
<DateTimePicker <input
value={value} type="date"
mode="date" value={value.toISOString().split('T')[0]}
display="default" onChange={(e) => {
onChange={(event, selectedDate) => {
setShowDatePicker(false); setShowDatePicker(false);
if (selectedDate) { if (e.target.value) {
onChange(selectedDate); onChange(new Date(e.target.value));
} }
}} }}
className="mt-1 p-1 border rounded"
/> />
)} )}
<Text style={{ fontSize: 14, color: '#666', marginTop: 4 }}> <p className="text-sm text-gray-500 mt-1">
Your date of birth is used to calculate your age. Your date of birth is used to calculate your age.
</Text> </p>
</View> </div>
)} )}
/> />
@ -121,35 +109,32 @@ export function AccountForm() {
control={control} control={control}
name="language" name="language"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Language</Text> <label className="block text-sm font-medium mb-1">Language</label>
<Picker <select
selectedValue={value} className="w-full p-2 border rounded"
onValueChange={onChange} value={value}
style={{ height: 50, width: '100%' }} onChange={(e) => onChange(e.target.value)}
> >
{languages.map((language) => ( {languages.map((language) => (
<Picker.Item key={language.value} label={language.label} value={language.value} /> <option key={language.value} value={language.value}>
{language.label}
</option>
))} ))}
</Picker> </select>
<Text style={{ fontSize: 14, color: '#666', marginTop: 4 }}> <p className="text-sm text-gray-500 mt-1">
This is the language that will be used in the dashboard. This is the language that will be used in the dashboard.
</Text> </p>
</View> </div>
)} )}
/> />
<TouchableOpacity <button
style={{ className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
backgroundColor: '#007AFF', onClick={handleSubmit(onSubmit)}
padding: 12,
borderRadius: 8,
alignItems: 'center',
}}
onPress={handleSubmit(onSubmit)}
> >
<Text style={{ color: 'white', fontWeight: '500' }}>Update account</Text> Update account
</TouchableOpacity> </button>
</View> </div>
); );
} }

View file

@ -1,19 +1,17 @@
import React from 'react'; import React from 'react';
import { View, Text } from 'react-native';
import { AccountForm } from './account-form'; import { AccountForm } from './account-form';
export default function SettingsAccountPage() { export default function SettingsAccountPage() {
return ( return (
<View style={{ gap: 24 }}> <div className="space-y-6">
<View> <div>
<Text style={{ fontSize: 18, fontWeight: '500' }}>Account</Text> <h2 className="text-lg font-medium">Account</h2>
<Text style={{ fontSize: 14, color: '#666' }}> <p className="text-sm text-gray-500">
Update your account settings. Set your preferred language and Update your account settings. Set your preferred language and timezone.
timezone. </p>
</Text> </div>
</View> <div className="border-t border-gray-200 my-4" />
<View style={{ height: 1, backgroundColor: '#ccc' }} />
<AccountForm /> <AccountForm />
</View> </div>
); );
} }

View file

@ -1,6 +1,4 @@
import React from 'react'; import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { Picker } from '@react-native-picker/picker';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
@ -32,75 +30,64 @@ export function AppearanceForm() {
}; };
return ( return (
<View> <div className="space-y-4">
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Font</Text> <label className="block text-sm font-medium mb-1">Font</label>
<Controller <Controller
control={control} control={control}
name="font" name="font"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<Picker <select
selectedValue={value} className="w-full p-2 border rounded"
onValueChange={onChange} value={value}
style={{ height: 50, width: 200 }} onChange={(e) => onChange(e.target.value)}
> >
<Picker.Item label="Inter" value="inter" /> <option value="inter">Inter</option>
<Picker.Item label="Manrope" value="manrope" /> <option value="manrope">Manrope</option>
<Picker.Item label="System" value="system" /> <option value="system">System</option>
</Picker> </select>
)} )}
/> />
<Text style={{ fontSize: 14, color: '#666' }}> <p className="text-sm text-gray-500">
Set the font you want to use in the dashboard. Set the font you want to use in the dashboard.
</Text> </p>
</View> </div>
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Theme</Text> <label className="block text-sm font-medium mb-1">Theme</label>
<Text style={{ fontSize: 14, color: '#666', marginBottom: 8 }}> <p className="text-sm text-gray-500 mb-2">
Select the theme for the dashboard. Select the theme for the dashboard.
</Text> </p>
<Controller <Controller
control={control} control={control}
name="theme" name="theme"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<View style={{ flexDirection: 'row', justifyContent: 'space-around' }}> <div className="flex space-x-4">
<TouchableOpacity <button
style={{ type="button"
padding: 12, className={`px-4 py-2 rounded ${value === 'light' ? 'bg-gray-200' : 'bg-white'}`}
backgroundColor: value === 'light' ? '#f0f0f0' : 'transparent', onClick={() => onChange('light')}
borderRadius: 8,
}}
onPress={() => onChange('light')}
> >
<Text>Light</Text> Light
</TouchableOpacity> </button>
<TouchableOpacity <button
style={{ type="button"
padding: 12, className={`px-4 py-2 rounded ${value === 'dark' ? 'bg-gray-200' : 'bg-white'}`}
backgroundColor: value === 'dark' ? '#f0f0f0' : 'transparent', onClick={() => onChange('dark')}
borderRadius: 8,
}}
onPress={() => onChange('dark')}
> >
<Text>Dark</Text> Dark
</TouchableOpacity> </button>
</View> </div>
)} )}
/> />
</View> </div>
<TouchableOpacity <button
style={{ className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
backgroundColor: '#007AFF', onClick={handleSubmit(onSubmit)}
padding: 12,
borderRadius: 8,
alignItems: 'center',
}}
onPress={handleSubmit(onSubmit)}
> >
<Text style={{ color: 'white', fontWeight: '500' }}>Update preferences</Text> Update preferences
</TouchableOpacity> </button>
</View> </div>
); );
} }

View file

@ -1,19 +1,17 @@
import React from 'react'; import React from 'react';
import { View, Text } from 'react-native';
import { AppearanceForm } from './appearance-form'; import { AppearanceForm } from './appearance-form';
export default function SettingsAppearancePage() { export default function SettingsAppearancePage() {
return ( return (
<View style={{ gap: 24 }}> <div className="space-y-6">
<View> <div>
<Text style={{ fontSize: 18, fontWeight: '500' }}>Appearance</Text> <h2 className="text-lg font-medium">Appearance</h2>
<Text style={{ fontSize: 14, color: '#666' }}> <p className="text-sm text-gray-500">
Customize the appearance of the app. Automatically switch between day Customize the appearance of the app. Automatically switch between day and night themes.
and night themes. </p>
</Text> </div>
</View> <div className="border-t border-gray-200 my-4" />
<View style={{ height: 1, backgroundColor: '#ccc' }} />
<AppearanceForm /> <AppearanceForm />
</View> </div>
); );
} }

View file

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native'; import { useNavigate, useLocation } from 'react-router-dom';
import { useNavigation, useRoute } from '@react-navigation/native';
interface SidebarNavProps { interface SidebarNavProps {
items: { items: {
@ -10,24 +9,22 @@ interface SidebarNavProps {
} }
export function SidebarNav({ items }: SidebarNavProps) { export function SidebarNav({ items }: SidebarNavProps) {
const navigation = useNavigation(); const navigate = useNavigate();
const route = useRoute(); const location = useLocation();
return ( return (
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}> <div className="flex flex-wrap gap-2">
{items.map((item) => ( {items.map((item) => (
<TouchableOpacity <button
key={item.href} key={item.href}
onPress={() => navigation.navigate(item.href)} onClick={() => navigate(item.href)}
style={{ className={`px-3 py-2 rounded-md text-sm font-medium ${
padding: 8, location.pathname === item.href ? 'bg-gray-100' : 'hover:bg-gray-50'
backgroundColor: route.name === item.href ? '#f0f0f0' : 'transparent', }`}
borderRadius: 4,
}}
> >
<Text>{item.title}</Text> {item.title}
</TouchableOpacity> </button>
))} ))}
</View> </div>
); );
} }

View file

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import { View, Text, Switch, TouchableOpacity } from 'react-native';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
@ -36,44 +35,43 @@ export function DisplayForm() {
}; };
return ( return (
<View> <div>
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 8 }}>Sidebar</Text> <h3 className="text-sm font-medium mb-2">Sidebar</h3>
<Text style={{ fontSize: 14, color: '#666', marginBottom: 16 }}> <p className="text-sm text-gray-500 mb-4">
Select the items you want to display in the sidebar. Select the items you want to display in the sidebar.
</Text> </p>
{items.map((item) => ( {items.map((item) => (
<Controller <Controller
key={item.id} key={item.id}
control={control} control={control}
name="items" name="items"
render={({ field }) => ( render={({ field }) => (
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 12 }}> <div className="flex items-center mb-3">
<Switch <input
value={field.value?.includes(item.id)} type="checkbox"
onValueChange={(checked) => { id={item.id}
const updatedValue = checked checked={field.value?.includes(item.id)}
onChange={(e) => {
const updatedValue = e.target.checked
? [...field.value, item.id] ? [...field.value, item.id]
: field.value?.filter((value) => value !== item.id); : field.value?.filter((value) => value !== item.id);
field.onChange(updatedValue); field.onChange(updatedValue);
}} }}
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/> />
<Text style={{ marginLeft: 8 }}>{item.label}</Text> <label htmlFor={item.id} className="ml-2 text-sm">
</View> {item.label}
</label>
</div>
)} )}
/> />
))} ))}
<TouchableOpacity <button
style={{ className="mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
backgroundColor: '#007AFF', onClick={handleSubmit(onSubmit)}
padding: 12,
borderRadius: 8,
alignItems: 'center',
marginTop: 16,
}}
onPress={handleSubmit(onSubmit)}
> >
<Text style={{ color: 'white', fontWeight: '500' }}>Update display</Text> Update display
</TouchableOpacity> </button>
</View> </div>
); );
} }

View file

@ -1,18 +1,17 @@
import React from 'react'; import React from 'react';
import { View, Text } from 'react-native';
import { DisplayForm } from './display-form'; import { DisplayForm } from './display-form';
export default function SettingsDisplayPage() { export default function SettingsDisplayPage() {
return ( return (
<View style={{ gap: 24 }}> <div className="space-y-6">
<View> <div>
<Text style={{ fontSize: 18, fontWeight: '500' }}>Display</Text> <h2 className="text-lg font-medium">Display</h2>
<Text style={{ fontSize: 14, color: '#666' }}> <p className="text-sm text-gray-500">
Turn items on or off to control what's displayed in the app. Turn items on or off to control what's displayed in the app.
</Text> </p>
</View> </div>
<View style={{ height: 1, backgroundColor: '#ccc' }} /> <div className="border-t border-gray-200 my-4" />
<DisplayForm /> <DisplayForm />
</View> </div>
); );
} }

View file

@ -1,18 +1,17 @@
import React from 'react'; import React from 'react';
import { View, Text } from 'react-native';
import { ProfileForm } from './profile-form'; import { ProfileForm } from './profile-form';
export default function SettingsProfilePage() { export default function SettingsProfilePage() {
return ( return (
<View style={{ gap: 24 }}> <div className="space-y-6">
<View> <div>
<Text style={{ fontSize: 18, fontWeight: '500' }}>Profile</Text> <h2 className="text-lg font-medium">Profile</h2>
<Text style={{ fontSize: 14, color: '#666' }}> <p className="text-sm text-gray-500">
This is how others will see you on the site. This is how others will see you on the site.
</Text> </p>
</View> </div>
<View style={{ height: 1, backgroundColor: '#ccc' }} /> <div className="border-t border-gray-200 my-4" />
<ProfileForm /> <ProfileForm />
</View> </div>
); );
} }

View file

@ -1,48 +1,47 @@
import React from 'react'; import React from 'react';
import { View, Text, ScrollView } from 'react-native';
import { SidebarNav } from './components/sidebar-nav'; import { SidebarNav } from './components/sidebar-nav';
const sidebarNavItems = [ const sidebarNavItems = [
{ {
title: "Profile", title: "Profile",
href: "/setttings", href: "/settings",
}, },
{ {
title: "Account", title: "Account",
href: "/setttings/account", href: "/settings/account",
}, },
{ {
title: "Appearance", title: "Appearance",
href: "/setttings/appearance", href: "/settings/appearance",
}, },
{ {
title: "Notifications", title: "Notifications",
href: "/setttings/notifications", href: "/settings/notifications",
}, },
{ {
title: "Display", title: "Display",
href: "/setttings/display", href: "/settings/display",
}, },
]; ];
export default function SettingsLayout({ children }) { export default function SettingsLayout({ children }: { children: React.ReactNode }) {
return ( return (
<ScrollView style={{ flex: 1 }}> <div className="flex-1 overflow-auto">
<View style={{ padding: 20 }}> <div className="p-5">
<View style={{ marginBottom: 24 }}> <div className="mb-6">
<Text style={{ fontSize: 24, fontWeight: 'bold' }}>Settings</Text> <h1 className="text-2xl font-bold">Settings</h1>
<Text style={{ fontSize: 14, color: '#666' }}> <p className="text-sm text-gray-500">
Manage your account settings and set e-mail preferences. Manage your account settings and set e-mail preferences.
</Text> </p>
</View> </div>
<View style={{ height: 1, backgroundColor: '#ccc', marginVertical: 24 }} /> <div className="border-t border-gray-200 my-6" />
<View style={{ flexDirection: 'row' }}> <div className="flex flex-col md:flex-row gap-6">
<View style={{ width: '25%', marginRight: 16 }}> <div className="w-full md:w-1/4">
<SidebarNav items={sidebarNavItems} /> <SidebarNav items={sidebarNavItems} />
</View> </div>
<View style={{ flex: 1 }}>{children}</View> <div className="flex-1">{children}</div>
</View> </div>
</View> </div>
</ScrollView> </div>
); );
} }

View file

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import { View, Text, Switch, TouchableOpacity } from 'react-native';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
@ -35,145 +34,145 @@ export function NotificationsForm() {
}; };
return ( return (
<View> <div className="space-y-4">
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Notify me about...</Text> <label className="block text-sm font-medium mb-1">Notify me about...</label>
<Controller <Controller
control={control} control={control}
name="type" name="type"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<View> <div className="space-y-2">
<TouchableOpacity <div className="flex items-center">
style={{ <input
flexDirection: 'row', type="radio"
alignItems: 'center', id="all"
marginBottom: 8, checked={value === 'all'}
}} onChange={() => onChange('all')}
onPress={() => onChange('all')} className="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500"
>
<View
style={{
width: 20,
height: 20,
borderRadius: 10,
borderWidth: 2,
borderColor: value === 'all' ? '#007AFF' : '#ccc',
marginRight: 8,
}}
/> />
<Text>All new messages</Text> <label htmlFor="all" className="ml-2">
</TouchableOpacity> All new messages
<TouchableOpacity </label>
style={{ </div>
flexDirection: 'row', <div className="flex items-center">
alignItems: 'center', <input
marginBottom: 8, type="radio"
}} id="mentions"
onPress={() => onChange('mentions')} checked={value === 'mentions'}
> onChange={() => onChange('mentions')}
<View className="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500"
style={{
width: 20,
height: 20,
borderRadius: 10,
borderWidth: 2,
borderColor: value === 'mentions' ? '#007AFF' : '#ccc',
marginRight: 8,
}}
/> />
<Text>Direct messages and mentions</Text> <label htmlFor="mentions" className="ml-2">
</TouchableOpacity> Direct messages and mentions
<TouchableOpacity </label>
style={{ </div>
flexDirection: 'row', <div className="flex items-center">
alignItems: 'center', <input
}} type="radio"
onPress={() => onChange('none')} id="none"
> checked={value === 'none'}
<View onChange={() => onChange('none')}
style={{ className="h-4 w-4 border-gray-300 text-blue-600 focus:ring-blue-500"
width: 20,
height: 20,
borderRadius: 10,
borderWidth: 2,
borderColor: value === 'none' ? '#007AFF' : '#ccc',
marginRight: 8,
}}
/> />
<Text>Nothing</Text> <label htmlFor="none" className="ml-2">
</TouchableOpacity> Nothing
</View> </label>
</div>
</div>
)} )}
/> />
</View> </div>
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 18, fontWeight: '500', marginBottom: 16 }}>Email Notifications</Text> <h3 className="text-sm font-medium mb-4">Email Notifications</h3>
<Controller <Controller
control={control} control={control}
name="communication_emails" name="communication_emails"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}> <div className="flex justify-between items-center mb-3">
<View> <div>
<Text style={{ fontSize: 16, fontWeight: '500' }}>Communication emails</Text> <label className="text-sm font-medium">Communication emails</label>
<Text style={{ fontSize: 14, color: '#666' }}>Receive emails about your account activity.</Text> <p className="text-sm text-gray-500">
</View> Receive emails about your account activity.
<Switch value={value} onValueChange={onChange} /> </p>
</View> </div>
<input
type="checkbox"
checked={value}
onChange={(e) => onChange(e.target.checked)}
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
</div>
)} )}
/> />
<Controller <Controller
control={control} control={control}
name="marketing_emails" name="marketing_emails"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}> <div className="flex justify-between items-center mb-3">
<View> <div>
<Text style={{ fontSize: 16, fontWeight: '500' }}>Marketing emails</Text> <label className="text-sm font-medium">Marketing emails</label>
<Text style={{ fontSize: 14, color: '#666' }}>Receive emails about new products, features, and more.</Text> <p className="text-sm text-gray-500">
</View> Receive emails about new products, features, and more.
<Switch value={value} onValueChange={onChange} /> </p>
</View> </div>
<input
type="checkbox"
checked={value}
onChange={(e) => onChange(e.target.checked)}
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
</div>
)} )}
/> />
<Controller <Controller
control={control} control={control}
name="social_emails" name="social_emails"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}> <div className="flex justify-between items-center mb-3">
<View> <div>
<Text style={{ fontSize: 16, fontWeight: '500' }}>Social emails</Text> <label className="text-sm font-medium">Social emails</label>
<Text style={{ fontSize: 14, color: '#666' }}>Receive emails for friend requests, follows, and more.</Text> <p className="text-sm text-gray-500">
</View> Receive emails for friend requests, follows, and more.
<Switch value={value} onValueChange={onChange} /> </p>
</View> </div>
<input
type="checkbox"
checked={value}
onChange={(e) => onChange(e.target.checked)}
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
</div>
)} )}
/> />
<Controller <Controller
control={control} control={control}
name="security_emails" name="security_emails"
render={({ field: { value } }) => ( render={({ field: { value } }) => (
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}> <div className="flex justify-between items-center">
<View> <div>
<Text style={{ fontSize: 16, fontWeight: '500' }}>Security emails</Text> <label className="text-sm font-medium">Security emails</label>
<Text style={{ fontSize: 14, color: '#666' }}>Receive emails about your account activity and security.</Text> <p className="text-sm text-gray-500">
</View> Receive emails about your account activity and security.
<Switch value={value} disabled /> </p>
</View> </div>
<input
type="checkbox"
checked={value}
disabled
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
</div>
)} )}
/> />
</View> </div>
<TouchableOpacity <button
style={{ className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
backgroundColor: '#007AFF', onClick={handleSubmit(onSubmit)}
padding: 12,
borderRadius: 8,
alignItems: 'center',
}}
onPress={handleSubmit(onSubmit)}
> >
<Text style={{ color: 'white', fontWeight: '500' }}>Update notifications</Text> Update notifications
</TouchableOpacity> </button>
</View> </div>
); );
} }

View file

@ -1,18 +1,17 @@
import React from 'react'; import React from 'react';
import { View, Text } from 'react-native';
import { NotificationsForm } from './notifications-form'; import { NotificationsForm } from './notifications-form';
export default function SettingsNotificationsPage() { export default function SettingsNotificationsPage() {
return ( return (
<View style={{ gap: 24 }}> <div className="space-y-6">
<View> <div>
<Text style={{ fontSize: 18, fontWeight: '500' }}>Notifications</Text> <h2 className="text-lg font-medium">Notifications</h2>
<Text style={{ fontSize: 14, color: '#666' }}> <p className="text-sm text-gray-500">
Configure how you receive notifications. Configure how you receive notifications.
</Text> </p>
</View> </div>
<View style={{ height: 1, backgroundColor: '#ccc' }} /> <div className="border-t border-gray-200 my-4" />
<NotificationsForm /> <NotificationsForm />
</View> </div>
); );
} }

View file

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import { View, Text, TextInput, TouchableOpacity } from 'react-native';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
@ -38,30 +37,25 @@ export function ProfileForm() {
}; };
return ( return (
<View> <div className="space-y-4">
<Controller <Controller
control={control} control={control}
name="username" name="username"
render={({ field: { onChange, onBlur, value }, fieldState: { error } }) => ( render={({ field: { onChange, onBlur, value }, fieldState: { error } }) => (
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Username</Text> <label className="block text-sm font-medium mb-1">Username</label>
<TextInput <input
style={{ className={`w-full p-2 border rounded ${error ? 'border-red-500' : 'border-gray-300'}`}
borderWidth: 1,
borderColor: error ? 'red' : '#ccc',
borderRadius: 4,
padding: 8,
}}
onBlur={onBlur} onBlur={onBlur}
onChangeText={onChange} onChange={(e) => onChange(e.target.value)}
value={value} value={value}
placeholder="Enter username" placeholder="Enter username"
/> />
{error && <Text style={{ color: 'red', fontSize: 12 }}>{error.message}</Text>} {error && <p className="text-red-500 text-xs mt-1">{error.message}</p>}
<Text style={{ fontSize: 14, color: '#666', marginTop: 4 }}> <p className="text-sm text-gray-500 mt-1">
This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days. This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days.
</Text> </p>
</View> </div>
)} )}
/> />
@ -69,26 +63,21 @@ export function ProfileForm() {
control={control} control={control}
name="email" name="email"
render={({ field: { onChange, onBlur, value }, fieldState: { error } }) => ( render={({ field: { onChange, onBlur, value }, fieldState: { error } }) => (
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Email</Text> <label className="block text-sm font-medium mb-1">Email</label>
<TextInput <input
style={{ className={`w-full p-2 border rounded ${error ? 'border-red-500' : 'border-gray-300'}`}
borderWidth: 1,
borderColor: error ? 'red' : '#ccc',
borderRadius: 4,
padding: 8,
}}
onBlur={onBlur} onBlur={onBlur}
onChangeText={onChange} onChange={(e) => onChange(e.target.value)}
value={value} value={value}
placeholder="Enter email" placeholder="Enter email"
keyboardType="email-address" type="email"
/> />
{error && <Text style={{ color: 'red', fontSize: 12 }}>{error.message}</Text>} {error && <p className="text-red-500 text-xs mt-1">{error.message}</p>}
<Text style={{ fontSize: 14, color: '#666', marginTop: 4 }}> <p className="text-sm text-gray-500 mt-1">
You can manage verified email addresses in your email settings. You can manage verified email addresses in your email settings.
</Text> </p>
</View> </div>
)} )}
/> />
@ -96,42 +85,30 @@ export function ProfileForm() {
control={control} control={control}
name="bio" name="bio"
render={({ field: { onChange, onBlur, value }, fieldState: { error } }) => ( render={({ field: { onChange, onBlur, value }, fieldState: { error } }) => (
<View style={{ marginBottom: 16 }}> <div className="mb-4">
<Text style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Bio</Text> <label className="block text-sm font-medium mb-1">Bio</label>
<TextInput <textarea
style={{ className={`w-full p-2 border rounded ${error ? 'border-red-500' : 'border-gray-300'}`}
borderWidth: 1,
borderColor: error ? 'red' : '#ccc',
borderRadius: 4,
padding: 8,
height: 100,
textAlignVertical: 'top',
}}
onBlur={onBlur} onBlur={onBlur}
onChangeText={onChange} onChange={(e) => onChange(e.target.value)}
value={value} value={value}
placeholder="Tell us a little bit about yourself" placeholder="Tell us a little bit about yourself"
multiline rows={4}
/> />
{error && <Text style={{ color: 'red', fontSize: 12 }}>{error.message}</Text>} {error && <p className="text-red-500 text-xs mt-1">{error.message}</p>}
<Text style={{ fontSize: 14, color: '#666', marginTop: 4 }}> <p className="text-sm text-gray-500 mt-1">
You can @mention other users and organizations to link to them. You can @mention other users and organizations to link to them.
</Text> </p>
</View> </div>
)} )}
/> />
<TouchableOpacity <button
style={{ className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
backgroundColor: '#007AFF', onClick={handleSubmit(onSubmit)}
padding: 12,
borderRadius: 8,
alignItems: 'center',
}}
onPress={handleSubmit(onSubmit)}
> >
<Text style={{ color: 'white', fontWeight: '500' }}>Update profile</Text> Update profile
</TouchableOpacity> </button>
</View> </div>
); );
} }

View file

@ -25,8 +25,8 @@ export default defineConfig(async () => ({
} }
: undefined, : undefined,
watch: { watch: {
// 3. tell vite to ignore watching `src-tauri` // 3. tell vite to ignore watching `src-rust`
ignored: ["**/src-tauri/**"], ignored: ["**/src-rust/**"],
}, },
}, },
})); }));