Skip to content

Commit

Permalink
barcode reader (#66)
Browse files Browse the repository at this point in the history
* native/feat: barcode reader initial component, install and app config, new assets

* native/barcode-scanner: feat: ui improvement, double tap handling, permission message reword

* native/barcode-scanner: feat: code scanner tracers

* native/barcode-scanner: feat: barcode scanner button and icon and modal

* barcode-scanner/feat: migration, generated types

* barcode-scanner/admin/feat: add barcodes from the panel, genericUpdate erro handling fix

* barcode-scanner/supabase/feat: new trigger, check barcode uniqueness on insert

* barcode-scanner/native/feat: useListBarcodes, barcode_modal params

* barcode-scanner/native/refactor: remove console logs

* barcode-scanner/native/fix: gen-types

* barcode-scanner/native/fix: gen-types

* barcode-scanner/native/fix: global caching/stale times

* barcode-scanner/supabase/fix: migration

* barcode-scanner/native/feat: working barcode scanner, error handling

* barcode-scanner/native/fix: navigate back when barcode is not recognized, improve tracers

* barcode-scanner/native/feat: better error display

* barcode-scanner/native/fix: typography align

---------

Co-authored-by: Michal Struck <[email protected]>
  • Loading branch information
michalstruck and Michal Struck authored Nov 21, 2023
1 parent e439504 commit 8033781
Show file tree
Hide file tree
Showing 24 changed files with 875 additions and 59 deletions.
2 changes: 1 addition & 1 deletion admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write .",
"gen-types": "supabase gen types typescript --local > ./src/lib/database.types.ts"
"gen-types": "npx supabase gen types typescript --local > ./src/lib/database.types.ts"
},
"devDependencies": {
"@fontsource/fira-mono": "^4.5.10",
Expand Down
18 changes: 18 additions & 0 deletions admin/src/lib/database.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export interface Database {
};
product: {
Row: {
barcodes: string[] | null;
company_id: number | null;
created_at: string;
id: number;
Expand All @@ -87,6 +88,7 @@ export interface Database {
unit: string;
};
Insert: {
barcodes?: string[] | null;
company_id?: number | null;
created_at?: string;
id?: number;
Expand All @@ -95,6 +97,7 @@ export interface Database {
unit?: string;
};
Update: {
barcodes?: string[] | null;
company_id?: number | null;
created_at?: string;
id?: number;
Expand Down Expand Up @@ -148,6 +151,21 @@ export interface Database {
}
];
};
test_tenant: {
Row: {
details: string | null;
id: number;
};
Insert: {
details?: string | null;
id?: number;
};
Update: {
details?: string | null;
id?: number;
};
Relationships: [];
};
worker: {
Row: {
company_id: number | null;
Expand Down
9 changes: 4 additions & 5 deletions admin/src/lib/genericUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { goto } from "$app/navigation";
import type { PostgrestBuilder } from "@supabase/postgrest-js";
import type { PostgrestError, PostgrestBuilder } from "@supabase/postgrest-js";

export const genericUpdate = async <T>(
builder: PostgrestBuilder<T>,
onSuccess: string,
onSuccess?: string,
setLoading?: (x: boolean) => void
) => {
try {
// loading = true;
setLoading && setLoading(true);
let { error } = await builder;
if (error) throw error;
goto(onSuccess);
if (onSuccess) goto(onSuccess);
} catch (error) {
if (error instanceof Error) alert(error.message);
if (error) alert((error as PostgrestError).message);
} finally {
setLoading && setLoading(false);
// loading = false;
}
};
44 changes: 41 additions & 3 deletions admin/src/routes/products/[id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { genericUpdate } from "$lib/genericUpdate";
import ScreenCard from "$lib/ScreenCard.svelte";
import { Label, Span, Input, Button } from "flowbite-svelte";
import { CloseCircleSolid } from "flowbite-svelte-icons";
let loading = false;
let product: Tables<"product"> | null = null;
Expand All @@ -17,12 +18,21 @@
name = product.name;
unit = product.unit;
steps = product.steps;
barcodes = product.barcodes ?? [];
})
);
let name: string | undefined = undefined;
let unit: string | undefined = undefined;
let steps: number[] = [];
let barcodes: string[] = [];
let newBarcode: string | null = null;
const addBarcode = () => {
if (!newBarcode) return;
barcodes = [...barcodes, newBarcode];
newBarcode = null;
};
const update = () =>
genericUpdate(
Expand All @@ -32,15 +42,16 @@
name,
unit,
steps,
barcodes: barcodes?.concat(newBarcode ?? []),
})
.eq("id", id),
"/products",
undefined,
(x) => (loading = x)
);
</script>

{#if product}
<ScreenCard header={"Product - " + product.name}>
<ScreenCard header={"Produkt - " + product.name}>
<form on:submit|preventDefault={update}>
<Label class="space-y-2">
<Span>Nazwa</Span>
Expand All @@ -58,8 +69,35 @@
<Input type="text" name="steps" required bind:value={steps[2]} />
</div>
</Label>
<div class="space-y-2 mt-2">
<Span>Kody</Span>
<div class="flex flex-col gap-4">
<div class="grid grid-cols-2 place-items-start gap-4">
<div class="col-span-2 flex gap-4 w-full">
<Input
type="text"
name="steps"
placeholder="Dodaj nowy kod"
class="h-min w-full"
bind:value={newBarcode}
/>
<Button type="submit" color="primary" class="shrink-0" on:click={addBarcode}
>Dodaj kod</Button
>
</div>
{#each barcodes as _barcode, i}
<Input type="text" name="steps" class="h-fit" required bind:value={barcodes[i]}>
<CloseCircleSolid
slot="right"
on:click={() => (barcodes = barcodes.filter((_, j) => i !== j))}
/>
</Input>
{/each}
</div>
</div>
</div>
<Button type="submit" class="mt-4" color="primary"
>{loading ? "Saving ..." : "Update product"}</Button
>{loading ? "Saving ..." : "Aktualizuj produkt"}</Button
>
</form>
</ScreenCard>
Expand Down
10 changes: 9 additions & 1 deletion native/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@
"bundler": "metro",
"favicon": "./assets/images/favicon.png"
},
"plugins": ["expo-router"],
"plugins": [
"expo-router",
[
"expo-camera",
{
"cameraPermission": "Pozwól $(PRODUCT_NAME) na dostęp do kamery."
}
]
],
"experiments": {
"typedRoutes": true
},
Expand Down
26 changes: 22 additions & 4 deletions native/app/(tabs)/[inventory]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from "react";
import { ScrollView, StyleSheet, View } from "react-native";

import { useLocalSearchParams } from "expo-router";
import { Link, useLocalSearchParams } from "expo-router";
import { SafeAreaView } from "react-native-safe-area-context";
import { Button } from "../../../components/Button";
import { ScanBarcodeIcon } from "../../../components/Icon";
import { InventoryListCard } from "../../../components/InventoryListCard";
import { Typography } from "../../../components/Typography";
import { useListRecords } from "../../../db";
Expand All @@ -27,6 +29,24 @@ export default function InventoryIdIndex() {
</View>
<View style={styles.listContainer}>
<View style={styles.date}></View>
<Link
href={{
pathname: "/barcode_modal",
params: { inventoryId },
}}
asChild
>
<Button
containerStyle={{
alignSelf: "flex-end",
marginBottom: 16,
}}
size="l"
type="primary"
>
<ScanBarcodeIcon size={34} />
</Button>
</Link>
{recordList.map(({ name, quantity, unit, id }) => (
// TODO - think of a clever way to check if these are not null, and let TS know
<InventoryListCard
Expand Down Expand Up @@ -57,9 +77,7 @@ const useStyles = createStyles((theme) =>
justifyContent: "center",
alignItems: "center",
},
listContainer: {
paddingHorizontal: theme.spacing * 4,
},
listContainer: { paddingHorizontal: theme.spacing * 4 },
scroll: {
width: "100%",
height: "100%",
Expand Down
2 changes: 1 addition & 1 deletion native/app/(tabs)/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const ListIndex = () => {
() => groupDaysByMonth(groupByDay(inventoryList)),
[inventoryList]
);
console.log(inventoryList?.map((inv) => inv.date));

if (!inventoryList || !months) return null;

return (
Expand Down
18 changes: 13 additions & 5 deletions native/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const ONE_SECOND = 1000;
const queryClient = new QueryClient({
defaultOptions: {
mutations: {
cacheTime: Infinity,
cacheTime: ONE_SECOND * 60 * 5,
retry: 100,
retryDelay: (attemptIndex) =>
Math.min(ONE_SECOND * 2 ** attemptIndex, 30 * ONE_SECOND),
Expand All @@ -56,9 +56,8 @@ const queryClient = new QueryClient({
retry: 100,
retryDelay: (attemptIndex) =>
Math.min(ONE_SECOND * 2 ** attemptIndex, 30 * ONE_SECOND),
cacheTime: Infinity,
staleTime: 60 * 60 * ONE_SECOND,
networkMode: "offlineFirst",
cacheTime: ONE_SECOND * 60 * 5,
staleTime: ONE_SECOND * 60,
},
},
});
Expand Down Expand Up @@ -111,7 +110,6 @@ export default function Root() {
onlineManager.setEventListener((setOnline) => {
return NetInfo.addEventListener((state) => {
setOnline(!!state.isConnected);
console.warn(onlineManager.isOnline());
});
});

Expand Down Expand Up @@ -167,6 +165,7 @@ export default function Root() {
header: (p) => <Header {...p} />,
}}
>
<Stack.Screen name="index" redirect />
<Stack.Screen
name="(start)"
options={{
Expand All @@ -183,6 +182,15 @@ export default function Root() {
headerShown: false,
}}
/>
{/* maybe a bottomsheet? */}
<Stack.Screen
name="barcode_modal"
options={{
presentation: "modal",
// needs a custom header
headerShown: false,
}}
/>
<Stack.Screen name="account" />
<Stack.Screen
name="new"
Expand Down
72 changes: 72 additions & 0 deletions native/app/barcode_modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Camera } from "expo-camera";
import React from "react";
import { ActivityIndicator, StyleSheet, View } from "react-native";

import { SafeAreaView } from "react-native-safe-area-context";
import { BarcodeScanner } from "../components/BarcodeScanner";
import { Button } from "../components/Button";

import { Typography } from "../components/Typography";

import { useLocalSearchParams } from "expo-router";
import { createStyles } from "../theme/useStyles";

export default function BarcodeModal() {
const styles = useStyles();

const { inventoryId } = useLocalSearchParams<{ inventoryId: string }>();

const [permission, requestPermission] = Camera.useCameraPermissions();

if (!permission) {
return (
<View style={styles.container}>
<ActivityIndicator size="large" color="#000" />
</View>
);
}

if (!permission?.granted) {
// Camera permissions are not granted yet
return (
<SafeAreaView
edges={["left", "right", "bottom"]}
style={styles.container}
>
<Typography
variant="l"
color="darkBlue"
style={{ textAlign: "center" }}
>
Aby skorzystać ze skanera kodów, pozwól aplikacji na dostęp do kamery.
</Typography>
<Button
onPress={requestPermission}
size="l"
type="primary"
shadow
containerStyle={{ marginTop: 16, width: 200, alignSelf: "center" }}
>
Zapytaj o dostęp
</Button>
</SafeAreaView>
);
}

return (
<SafeAreaView edges={["left", "right", "bottom"]} style={styles.container}>
<BarcodeScanner inventoryId={+inventoryId} />
</SafeAreaView>
);
}

const useStyles = createStyles((theme) =>
StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
backgroundColor: theme.colors.lightBlue,
height: "100%",
},
})
);
9 changes: 1 addition & 8 deletions native/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import React from "react";
import { View } from "react-native";

import { Typography } from "../components/Typography";

// TODO Landing page
export default function Landing() {
return (
<View>
<Typography>test5</Typography>
</View>
);
return <View />;
}
Binary file added native/assets/images/camera-switch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added native/assets/images/scan-barcode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 8033781

Please sign in to comment.