Add beenvoice mobile companion app with full dark mode support.
Expo app with dashboard, time clock, invoices, and settings — native tabs, glass UI, theme-aware components, and iOS Live Activities. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
|
||||
import { Input } from "@/components/ui/Input";
|
||||
import { fonts, spacing } from "@/constants/theme";
|
||||
import { useAccounts } from "@/contexts/AccountsContext";
|
||||
import { useAppTheme } from "@/contexts/ThemeContext";
|
||||
import { normalizeInstanceUrl } from "@/lib/instance-url";
|
||||
|
||||
type InstanceUrlFieldProps = {
|
||||
onSaved?: (url: string) => void;
|
||||
};
|
||||
|
||||
export function InstanceUrlField({ onSaved }: InstanceUrlFieldProps) {
|
||||
const { colors } = useAppTheme();
|
||||
const { apiUrl, setInstanceUrl } = useAccounts();
|
||||
const [value, setValue] = useState(apiUrl);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(apiUrl);
|
||||
}, [apiUrl]);
|
||||
|
||||
async function commit() {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed || trimmed === apiUrl) {
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = normalizeInstanceUrl(trimmed);
|
||||
if (!normalized) {
|
||||
setError("Enter a valid URL like beenvoice.app or localhost:3000");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const saved = await setInstanceUrl(trimmed);
|
||||
setValue(saved);
|
||||
setError(null);
|
||||
onSaved?.(saved);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Could not save server URL");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.wrapper}>
|
||||
<Input
|
||||
label="Server instance"
|
||||
value={value}
|
||||
onChangeText={setValue}
|
||||
onBlur={commit}
|
||||
onSubmitEditing={commit}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
keyboardType="url"
|
||||
placeholder="beenvoice.app or localhost:3000"
|
||||
error={error ?? undefined}
|
||||
/>
|
||||
<Text style={[styles.hint, { color: colors.mutedForeground }]}>
|
||||
Point the app at your beenvoice server. Use your Mac's LAN IP on a physical device.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
gap: spacing.xs,
|
||||
},
|
||||
hint: {
|
||||
fontSize: 12,
|
||||
fontFamily: fonts.body,
|
||||
lineHeight: 16,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user