From 4c7ae9de796929a098bf0e7685ebbee3a2d92fb3 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 15 Oct 2025 16:04:54 -0700 Subject: [PATCH] Update job listing pag.e --- components/jobs-list/job-list-item.jsx | 160 +++++++++++++++++++++---- components/jobs-list/jobs-list.jsx | 1 + package-lock.json | 10 ++ package.json | 1 + 4 files changed, 148 insertions(+), 24 deletions(-) diff --git a/components/jobs-list/job-list-item.jsx b/components/jobs-list/job-list-item.jsx index 1e17f79..670ed20 100644 --- a/components/jobs-list/job-list-item.jsx +++ b/components/jobs-list/job-list-item.jsx @@ -1,6 +1,10 @@ +import { BlurView } from "expo-blur"; +import * as Haptics from "expo-haptics"; import { useRouter } from "expo-router"; +import React, { memo, useCallback } from "react"; import { useTranslation } from "react-i18next"; -import { Button, List, Text, useTheme } from "react-native-paper"; +import { Platform, Pressable, StyleSheet, View } from "react-native"; +import { IconButton, Text, useTheme } from "react-native-paper"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { logImEXEvent } from "../../firebase/firebase.analytics"; @@ -13,11 +17,13 @@ const mapDispatchToProps = (dispatch) => ({ openImagePicker: (id) => dispatch(openImagePicker(id)), }); -export function JobListItem({ openImagePicker, item }) { +function JobListItemComponent({ openImagePicker, item }) { const { t } = useTranslation(); const router = useRouter(); const theme = useTheme(); - const onPress = () => { + + const onPress = useCallback(() => { + Haptics.selectionAsync(); logImEXEvent("imexmobile_view_job_detail"); router.navigate({ pathname: `/jobs/${item.id}`, @@ -25,33 +31,139 @@ export function JobListItem({ openImagePicker, item }) { title: item.ro_number || t("general.labels.na"), }, }); - }; + }, [item.id, item.ro_number, router, t]); - const handleUpload = () => { + const handleUpload = useCallback(() => { + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); openImagePicker(item.id); - }; + }, [openImagePicker, item.id]); + + const ownerName = `${item.ownr_fn || ""} ${item.ownr_ln || ""}`.trim(); + const company = item.ownr_co_nm || ""; + const vehicle = `${item.v_model_yr || ""} ${item.v_make_desc || ""} ${ + item.v_model_desc || "" + }`.trim(); + const roNumber = item.ro_number || t("general.labels.na"); return ( - ( - - {item.ro_number || t("general.labels.na")} - - )} - right={() => ( - - )} - /> + style={({ pressed }) => [styles.pressable, pressed && { opacity: 0.85 }]} + > + + + {/* Translucent overlay for glass effect */} + + + + + {roNumber} • {ownerName} + {ownerName && company ? " • " : ""} + {company} + + + + + + {!!vehicle && ( + + {vehicle} + + )} + + + + + ); } +const styles = StyleSheet.create({ + pressable: { + marginHorizontal: 12, + marginVertical: 6, + }, + outerShadow: { + borderRadius: 20, + shadowColor: "#000", + shadowOpacity: 0.12, + shadowRadius: 8, + shadowOffset: { width: 0, height: 4 }, + elevation: 3, + }, + blurContainer: { + overflow: "hidden", + borderRadius: 20, + }, + glassCard: { + padding: 14, + borderRadius: 20, + borderWidth: StyleSheet.hairlineWidth, + backdropFilter: "blur(20px)", // web only + }, + headerRow: { + flexDirection: "row", + alignItems: "flex-start", + justifyContent: "space-between", + marginBottom: 4, + }, + leftHeader: { + flex: 1, + paddingRight: 8, + }, + roNumber: { + fontSize: 18, + fontWeight: "600", + letterSpacing: 0.5, + }, + ownerInfo: { + marginTop: 2, + fontSize: 15, + fontWeight: "500", + opacity: 0.9, + }, + uploadButton: { + margin: 0, + }, + body: { + marginTop: 2, + }, + ownerText: { + fontWeight: "600", + }, +}); + +const JobListItem = memo( + JobListItemComponent, + (prev, next) => prev.item.id === next.item.id +); + export default connect(mapStateToProps, mapDispatchToProps)(JobListItem); diff --git a/components/jobs-list/jobs-list.jsx b/components/jobs-list/jobs-list.jsx index 65d138b..eb44f1c 100644 --- a/components/jobs-list/jobs-list.jsx +++ b/components/jobs-list/jobs-list.jsx @@ -75,6 +75,7 @@ export function JobListComponent({ bodyshop }) { } style={{ flex: 1 }} data={jobs} + keyExtractor={(item) => item.id?.toString()} renderItem={(object) => } /> diff --git a/package-lock.json b/package-lock.json index b98b6e2..6d8261c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "dinero.js": "^1.9.1", "expo": "~54.0.12", "expo-application": "~7.0.7", + "expo-blur": "~13.0.0", "expo-constants": "~18.0.9", "expo-dev-client": "~6.0.13", "expo-file-system": "~19.0.16", @@ -8134,6 +8135,15 @@ "react-native": "*" } }, + "node_modules/expo-blur": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/expo-blur/-/expo-blur-13.0.3.tgz", + "integrity": "sha512-z5W9ZGlG6ZiRLuoJZG1AHRvjK8j+2+nc/mtvEtyAa8T/8iTNpUnX4eC8xXDoTL/H4y2pc3cHvytDjCXJG26pcQ==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-constants": { "version": "18.0.9", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz", diff --git a/package.json b/package.json index 764c727..3c1358f 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "dinero.js": "^1.9.1", "expo": "~54.0.12", "expo-application": "~7.0.7", + "expo-blur": "~13.0.0", "expo-constants": "~18.0.9", "expo-dev-client": "~6.0.13", "expo-file-system": "~19.0.16",