diff --git a/babel-translations.babel b/babel-translations.babel
index 61dc093..11c2b9d 100644
--- a/babel-translations.babel
+++ b/babel-translations.babel
@@ -126,6 +126,48 @@
actions
+
+ add
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ cancel
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
signout
false
@@ -253,6 +295,32 @@
jobdetail
+
+ actions
+
+
+ addnote
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
labels
@@ -4883,6 +4951,278 @@
+
+ notes
+
+
+ fields
+
+
+ critical
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ pinned
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ private
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ text
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ types
+
+
+ customer
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ general
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ office
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ paint
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ parts
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ shop
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ supplement
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
+
+
+
+ labels
+
+
+ newnoteplaceholder
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
+
+
diff --git a/components/job-notes/job-notes.jsx b/components/job-notes/job-notes.jsx
index e33a53e..e76a14f 100644
--- a/components/job-notes/job-notes.jsx
+++ b/components/job-notes/job-notes.jsx
@@ -3,14 +3,18 @@ import { useQuery } from "@apollo/client";
import { AntDesign } from "@expo/vector-icons";
import { useGlobalSearchParams } from "expo-router";
import { DateTime } from "luxon";
-import React from "react";
+import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { FlatList, RefreshControl, View } from "react-native";
-import { ActivityIndicator, Card, Text } from "react-native-paper";
+import { ActivityIndicator, Button, Card, Text } from "react-native-paper";
import ErrorDisplay from "../error/error-display";
+import NewNoteModal from "./new-note-modal";
export default function JobNotes() {
const { jobId } = useGlobalSearchParams();
+ const [noteModalVisible, setNoteModalVisible] = useState(false);
+ const showNoteModal = () => setNoteModalVisible(true);
+ const hideNoteModal = () => setNoteModalVisible(false);
const { loading, error, data, refetch } = useQuery(GET_JOB_BY_PK, {
variables: {
@@ -19,6 +23,11 @@ export default function JobNotes() {
skip: !jobId,
});
+ const handleNoteCreated = useCallback(() => {
+ hideNoteModal();
+ refetch();
+ }, [refetch]);
+
const { t } = useTranslation();
const onRefresh = async () => {
return refetch();
@@ -36,24 +45,40 @@ export default function JobNotes() {
}
const job = data.jobs_by_pk;
- if (job.notes.length === 0)
- return (
-
-
- {t("jobdetail.labels.nojobnotes")}
-
-
- );
-
return (
-
- }
- style={{ flex: 1 }}
- data={job.notes}
- renderItem={(object) => }
- />
+
+
+ {job.notes.length === 0 ? (
+
+
+ {t("jobdetail.labels.nojobnotes")}
+
+
+ ) : (
+
+ }
+ style={{ flex: 1 }}
+ data={job.notes}
+ renderItem={(object) => }
+ />
+ )}
+
+
);
}
@@ -72,7 +97,7 @@ function NoteListItem({ item }) {
>
{item.private && (
void - called when modal closed without save
+ * - onCreated: (note) => void - called after successful creation
+ * - relatedRos: array of job objects to optionally attach note to (excludes current job)
+ */
+
+const mapStateToProps = createStructuredSelector({
+ currentUser: selectCurrentUser,
+});
+const mapDispatchToProps = (dispatch) => ({});
+
+const NewNoteModal = ({
+ jobId,
+ visible,
+ onDismiss,
+ onCreated,
+ relatedRos = [],
+ existingNote,
+ currentUser,
+}) => {
+ const theme = useTheme();
+ const { t } = useTranslation();
+ const insets = useSafeAreaInsets();
+ const [keyboardSpace, setKeyboardSpace] = useState(0);
+
+ // Enable LayoutAnimation on Android
+ if (
+ Platform.OS === "android" &&
+ UIManager.setLayoutAnimationEnabledExperimental
+ ) {
+ try {
+ UIManager.setLayoutAnimationEnabledExperimental(true);
+ } catch (_) {}
+ }
+
+ useEffect(() => {
+ const showSub = Keyboard.addListener("keyboardWillShow", (e) => {
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
+ setKeyboardSpace(e.endCoordinates.height);
+ });
+ const hideSub = Keyboard.addListener("keyboardWillHide", () => {
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
+ setKeyboardSpace(0);
+ });
+ // Fallback for Android (keyboardWillShow not always fired)
+ const showSubAndroid = Keyboard.addListener("keyboardDidShow", (e) => {
+ if (Platform.OS === "android") {
+ setKeyboardSpace(e.endCoordinates.height);
+ }
+ });
+ const hideSubAndroid = Keyboard.addListener("keyboardDidHide", () => {
+ if (Platform.OS === "android") setKeyboardSpace(0);
+ });
+ return () => {
+ showSub.remove();
+ hideSub.remove();
+ showSubAndroid.remove();
+ hideSubAndroid.remove();
+ };
+ }, []);
+
+ const [insertNote, { loading }] = useMutation(INSERT_NEW_NOTE, {
+ refetchQueries: [{ query: GET_JOB_BY_PK, variables: { id: jobId } }],
+ awaitRefetchQueries: true,
+ });
+
+ // Filter out current job id
+ const filteredRelatedRos = relatedRos.filter((j) => j.id !== jobId);
+
+ const initialValues = {
+ critical: false,
+ private: false,
+ pinned: false,
+ type: "general",
+ text: "",
+ relatedros: {},
+ };
+
+ const handleSubmit = async (values, helpers) => {
+ try {
+ const noteInput = {
+ jobid: jobId,
+ text: values.text.trim(),
+ critical: values.critical,
+ private: values.private,
+ pinned: values.pinned,
+ type: values.type,
+ created_by: currentUser?.email,
+ };
+ console.log("*** ~ handleSubmit ~ noteInput:", noteInput);
+ // TODO: If backend supports attaching note to multiple related ROs, perform additional mutation(s)
+ // here after creating the base note. This may involve an insert into a join table like note_jobs.
+ // values.relatedros contains boolean flags keyed by job id.
+ const { data } = await insertNote({
+ variables: { note: [noteInput] },
+ });
+ const created = data?.insert_notes?.returning?.[0];
+ if (onCreated && created) onCreated(created);
+ helpers.resetForm();
+ } catch (e) {
+ helpers.setStatus({ error: e.message });
+ }
+ };
+
+ return (
+
+
+
+ {
+ const errors = {};
+ if (!values.text || !values.text.trim()) {
+ errors.text = t("general.validation.required");
+ }
+ return errors;
+ }}
+ onSubmit={handleSubmit}
+ >
+ {({
+ values,
+ errors,
+ touched,
+ setFieldValue,
+ handleSubmit,
+ status,
+ }) => (
+
+
+ {existingNote
+ ? t("objects.notes.labels.editnote")
+ : t("jobdetail.actions.addnote")}
+
+
+
+ setFieldValue("critical", v)}
+ />
+ setFieldValue("private", v)}
+ />
+ setFieldValue("pinned", v)}
+ />
+
+
+ {[
+ "general",
+ "customer",
+ "shop",
+ "office",
+ "parts",
+ "paint",
+ "supplement",
+ ].map((option) => (
+ setFieldValue("type", option)}
+ style={styles.chip}
+ >
+ {t(`objects.notes.fields.types.${option}`)}
+
+ ))}
+
+ setFieldValue("text", v)}
+ multiline
+ mode="outlined"
+ placeholder={t("objects.notes.labels.newnoteplaceholder")}
+ style={styles.textArea}
+ />
+ {touched.text && errors.text && (
+
+ {errors.text}
+
+ )}
+ {status?.error && (
+
+ {status.error}
+
+ )}
+ {!existingNote && filteredRelatedRos.length > 0 && (
+
+
+ {t("objects.notes.labels.addtorelatedro")}
+
+ {filteredRelatedRos.map((j) => (
+
+ setFieldValue(`relatedros.${j.id}`, v)
+ }
+ />
+ ))}
+
+ )}
+
+
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+const SwitchWithLabel = ({ label, value, onValueChange }) => (
+
+
+ {label}
+
+);
+
+const styles = StyleSheet.create({
+ modalContainer: {
+ marginHorizontal: 24,
+ borderRadius: 16,
+ //paddingVertical: 12,
+ paddingHorizontal: 12,
+ maxHeight: "60j%",
+
+ //minHeight: 200,
+ },
+ kbWrapper: {
+ flex: 1,
+ },
+ modalTitle: {
+ paddingHorizontal: 4,
+ paddingBottom: 8,
+ },
+ formScroll: {
+ marginTop: 8,
+ },
+ rowWrap: {
+ flexDirection: "row",
+ flexWrap: "wrap",
+ justifyContent: "space-between",
+ marginBottom: 12,
+ },
+ checkboxItem: {
+ flexDirection: "row",
+ alignItems: "center",
+ marginRight: 12,
+ marginVertical: 4,
+ },
+ checkboxLabel: {
+ fontSize: 14,
+ marginLeft: 8,
+ },
+ typeChips: {
+ flexDirection: "row",
+ flexWrap: "wrap",
+ marginBottom: 12,
+ },
+ chip: {
+ margin: 4,
+ },
+ textArea: {
+ minHeight: 160,
+ marginBottom: 8,
+ },
+ relatedRosContainer: {
+ marginTop: 16,
+ },
+ relatedRosLabel: {
+ fontWeight: "600",
+ marginBottom: 8,
+ },
+ actions: {
+ flexDirection: "row",
+ justifyContent: "flex-end",
+ marginTop: 16,
+ },
+ scrollContent: {
+ // paddingBottom: 24,
+ },
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(NewNoteModal);
diff --git a/components/job-status-selector/JobStatusSelector.tsx b/components/job-status-selector/job-status-selector.jsx
similarity index 77%
rename from components/job-status-selector/JobStatusSelector.tsx
rename to components/job-status-selector/job-status-selector.jsx
index f411880..c04807a 100644
--- a/components/job-status-selector/JobStatusSelector.tsx
+++ b/components/job-status-selector/job-status-selector.jsx
@@ -5,34 +5,16 @@ import { useLocalSearchParams } from "expo-router";
import React, { useCallback, useState } from "react";
import { FlatList, StyleSheet, View } from "react-native";
import {
- Button,
- Divider,
- List,
- Modal,
- Portal,
- Text,
- useTheme,
+ Button,
+ Divider,
+ List,
+ Modal,
+ Portal,
+ Text,
+ useTheme,
} from "react-native-paper";
-/**
- * JobStatusSelector component contract
- * Props:
- * - statuses: string[] (list of available statuses)
- * - currentStatus: string (currently applied status)
- * - onSelect: (status: string) => void (fires when user selects a status)
- * - label?: string (optional label for trigger button)
- */
-export interface JobStatusSelectorProps {
- statuses: string[];
- currentStatus: string | undefined;
- onSelect: (status: string) => void;
- label?: string;
- disabled?: boolean;
-}
-
-const keyExtractor = (item: string) => item;
-
-export const JobStatusSelector: React.FC = ({
+export const JobStatusSelector = ({
statuses,
currentStatus,
onSelect,
@@ -49,7 +31,7 @@ export const JobStatusSelector: React.FC = ({
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS);
const handleSelect = useCallback(
- async (status: string) => {
+ async (status) => {
Haptics.selectionAsync().catch(() => {});
hide();
await updateJobStatus({
@@ -89,7 +71,7 @@ export const JobStatusSelector: React.FC = ({
item}
renderItem={({ item }) => {
const selected = item === currentStatus;
return (
@@ -155,12 +137,3 @@ const styles = StyleSheet.create({
});
export default JobStatusSelector;
-
-/**
- * Usage example:
- * console.log('Status changed to', newStatus)}
- * />
- */
diff --git a/components/job-tombstone/job-tombstone.jsx b/components/job-tombstone/job-tombstone.jsx
index 4856bb9..e452107 100644
--- a/components/job-tombstone/job-tombstone.jsx
+++ b/components/job-tombstone/job-tombstone.jsx
@@ -5,17 +5,12 @@ import { useLocalSearchParams } from "expo-router";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { RefreshControl, ScrollView, StyleSheet, View } from "react-native";
-import {
- ActivityIndicator,
- Card,
- Chip,
- Text,
- useTheme,
-} from "react-native-paper";
+import { ActivityIndicator, Card, Chip, Text } from "react-native-paper";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import DataLabelComponent from "../data-label/data-label";
-import { JobStatusSelector } from "../job-status-selector/JobStatusSelector";
+import ErrorDisplay from "../error/error-display";
+import { JobStatusSelector } from "../job-status-selector/job-status-selector";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -31,9 +26,6 @@ function JobTombstone({ bodyshop }) {
},
skip: !jobId,
});
- console.log("JobTombstone render");
-
- const theme = useTheme();
const { t } = useTranslation();
const onRefresh = async () => {
@@ -80,6 +72,11 @@ function JobTombstone({ bodyshop }) {
if (loading) {
return ;
}
+
+ if (error) {
+ return ;
+ }
+
if (!data.jobs_by_pk) {
return (
diff --git a/graphql/notes.queries.js b/graphql/notes.queries.js
new file mode 100644
index 0000000..f920296
--- /dev/null
+++ b/graphql/notes.queries.js
@@ -0,0 +1,22 @@
+import { gql } from "graphql-tag";
+
+// TODO: Confirm backend schema for notes table. Assumed fields:
+// id (uuid), jobid (uuid), text (String!), critical (Boolean), private (Boolean), pinned (Boolean), type (String), created_at (timestamptz), created_by (String)
+// relatedros is assumed handled via separate linking if required.
+
+export const INSERT_NEW_NOTE = gql`
+ mutation INSERT_NEW_NOTE($note: [notes_insert_input!]!) {
+ insert_notes(objects: $note) {
+ returning {
+ id
+ text
+ critical
+ private
+ pinned
+ type
+ created_at
+ created_by
+ }
+ }
+ }
+`;
diff --git a/package-lock.json b/package-lock.json
index 7a5a884..0d0b046 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,7 +19,7 @@
"@reduxjs/toolkit": "^2.9.1",
"axios": "^1.12.2",
"dinero.js": "^1.9.1",
- "expo": "54.0.20",
+ "expo": "54.0.21",
"expo-application": "~7.0.7",
"expo-constants": "~18.0.10",
"expo-dev-client": "~6.0.16",
@@ -2305,9 +2305,9 @@
}
},
"node_modules/@expo/metro-config": {
- "version": "54.0.7",
- "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.7.tgz",
- "integrity": "sha512-bXluEygLrd7cIh/erpjIIC2xDeanaebcwzF+DUMD5vAqHU3o0QXAF3jRV/LsjXZud9V5eRpyCRZ3tLQL0iv8WA==",
+ "version": "54.0.8",
+ "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.8.tgz",
+ "integrity": "sha512-rCkDQ8IT6sgcGNy48O2cTE4NlazCAgAIsD5qBsNPJLZSS0XbaILvAgGsFt/4nrx0GMGj6iQcOn5ifwV4NssTmw==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.20.0",
@@ -8184,19 +8184,19 @@
"license": "MIT"
},
"node_modules/expo": {
- "version": "54.0.20",
- "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.20.tgz",
- "integrity": "sha512-mWHky+H63W60P5Oo+VbtqzF2sLvdaoSSwG57H9rlq1DrgIla++QJZuwJkXXo55lYPymVmkVhwG6FjWYKKylwpw==",
+ "version": "54.0.21",
+ "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.21.tgz",
+ "integrity": "sha512-I3kzMNW/43a71pt6hT0Zebd2zAPIMMeucUDDEdfUKYrzzTRwISZfVAv0dp8GWKHHDjZsy+FjE4RQCMdyKmiDeQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.20.0",
- "@expo/cli": "54.0.13",
+ "@expo/cli": "54.0.14",
"@expo/config": "~12.0.10",
"@expo/config-plugins": "~54.0.2",
"@expo/devtools": "0.1.7",
"@expo/fingerprint": "0.15.2",
"@expo/metro": "~54.1.0",
- "@expo/metro-config": "54.0.7",
+ "@expo/metro-config": "54.0.8",
"@expo/vector-icons": "^15.0.3",
"@ungap/structured-clone": "^1.3.0",
"babel-preset-expo": "~54.0.6",
@@ -8206,7 +8206,7 @@
"expo-font": "~14.0.9",
"expo-keep-awake": "~15.0.7",
"expo-modules-autolinking": "3.0.19",
- "expo-modules-core": "3.0.22",
+ "expo-modules-core": "3.0.23",
"pretty-format": "^29.7.0",
"react-refresh": "^0.14.2",
"whatwg-url-without-unicode": "8.0.0-3"
@@ -8516,9 +8516,9 @@
}
},
"node_modules/expo-modules-core": {
- "version": "3.0.22",
- "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.22.tgz",
- "integrity": "sha512-FqG5oelITFTLcIfGwoJP8Qsk65be/eiEjz354NdAurnhFARHAVYOOIsUehArvm75ISdZOIZEaTSjCudmkA3kKg==",
+ "version": "3.0.23",
+ "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.23.tgz",
+ "integrity": "sha512-NYHi5LK/cdIyOjK9ZQAgfDPCOqER26cIbU3gzsce7YdnsmlNFR0qMfWOj9zAmaFBviC2kCkCOmitwk4357Td3Q==",
"license": "MIT",
"dependencies": {
"invariant": "^2.2.4"
@@ -8638,9 +8638,9 @@
}
},
"node_modules/expo-server": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.2.tgz",
- "integrity": "sha512-QlQLjFuwgCiBc+Qq0IyBBHiZK1RS0NJSsKVB5iECMJrR04q7PhkaF7dON0fhvo00COy4fT9rJ5brrJDpFro/gA==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.3.tgz",
+ "integrity": "sha512-SOwdzM/BFAL+vTFlUDJG6ljhyk6TyTl+LRK3ubGmN+Pf18ENRqKj37U8krc5vH926sAsB3IFcE8kJEYf4dG7PA==",
"license": "MIT",
"engines": {
"node": ">=20.16.0"
@@ -8786,9 +8786,9 @@
}
},
"node_modules/expo/node_modules/@expo/cli": {
- "version": "54.0.13",
- "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.13.tgz",
- "integrity": "sha512-wUJVTByZzDN0q8UjXDlu6WD2BWoTJCKVVBGUBNmvViDX4FhnESwefmtXPoO54QUUKs6vY89WZryHllGArGfLLw==",
+ "version": "54.0.14",
+ "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.14.tgz",
+ "integrity": "sha512-M7QW/GHx1FJg+CGgChGKerYXmCGWDskJ8S6w+8m49IBZ41CMDeWRH5snQkFoGCttF8WnzhGiX+nu69AFnEuDHQ==",
"license": "MIT",
"dependencies": {
"@0no-co/graphql.web": "^1.0.8",
@@ -8801,7 +8801,7 @@
"@expo/json-file": "^10.0.7",
"@expo/mcp-tunnel": "~0.0.7",
"@expo/metro": "~54.1.0",
- "@expo/metro-config": "~54.0.7",
+ "@expo/metro-config": "~54.0.8",
"@expo/osascript": "^2.3.7",
"@expo/package-manager": "^1.9.8",
"@expo/plist": "^0.4.7",
@@ -8824,7 +8824,7 @@
"connect": "^3.7.0",
"debug": "^4.3.4",
"env-editor": "^0.4.1",
- "expo-server": "^1.0.2",
+ "expo-server": "^1.0.3",
"freeport-async": "^2.0.0",
"getenv": "^2.0.0",
"glob": "^10.4.2",
diff --git a/translations/en-US/common.json b/translations/en-US/common.json
index 4fba6dc..579fe1f 100644
--- a/translations/en-US/common.json
+++ b/translations/en-US/common.json
@@ -12,6 +12,8 @@
},
"general": {
"actions": {
+ "add": "Add",
+ "cancel": "Cancel",
"signout": "Sign Out"
},
"labels": {
@@ -26,6 +28,9 @@
}
},
"jobdetail": {
+ "actions": {
+ "addnote": "Add Note"
+ },
"labels": {
"claiminformation": "Claim Information",
"dates": "Dates",
@@ -286,6 +291,26 @@
"labels": {
"inproduction": "In Production"
}
+ },
+ "notes": {
+ "fields": {
+ "critical": "Critical?",
+ "pinned": "Pinned?",
+ "private": "Private?",
+ "text": "Note Text",
+ "types": {
+ "customer": "Customer",
+ "general": "General",
+ "office": "Office",
+ "paint": "Paint",
+ "parts": "Parts",
+ "shop": "Shop",
+ "supplement": "Supplement"
+ }
+ },
+ "labels": {
+ "newnoteplaceholder": "Enter a new note..."
+ }
}
},
"production": {
diff --git a/translations/es-MX/common.json b/translations/es-MX/common.json
index 8bc4793..6e2cb7f 100644
--- a/translations/es-MX/common.json
+++ b/translations/es-MX/common.json
@@ -12,6 +12,8 @@
},
"general": {
"actions": {
+ "add": "",
+ "cancel": "",
"signout": ""
},
"labels": {
@@ -26,6 +28,9 @@
}
},
"jobdetail": {
+ "actions": {
+ "addnote": ""
+ },
"labels": {
"claiminformation": "",
"dates": "",
@@ -286,6 +291,26 @@
"labels": {
"inproduction": ""
}
+ },
+ "notes": {
+ "fields": {
+ "critical": "",
+ "pinned": "",
+ "private": "",
+ "text": "",
+ "types": {
+ "customer": "",
+ "general": "",
+ "office": "",
+ "paint": "",
+ "parts": "",
+ "shop": "",
+ "supplement": ""
+ }
+ },
+ "labels": {
+ "newnoteplaceholder": ""
+ }
}
},
"production": {
diff --git a/translations/fr-CA/common.json b/translations/fr-CA/common.json
index d5b903f..a1afa92 100644
--- a/translations/fr-CA/common.json
+++ b/translations/fr-CA/common.json
@@ -12,6 +12,8 @@
},
"general": {
"actions": {
+ "add": "",
+ "cancel": "",
"signout": ""
},
"labels": {
@@ -26,6 +28,9 @@
}
},
"jobdetail": {
+ "actions": {
+ "addnote": ""
+ },
"labels": {
"claiminformation": "",
"dates": "",
@@ -286,6 +291,26 @@
"labels": {
"inproduction": ""
}
+ },
+ "notes": {
+ "fields": {
+ "critical": "",
+ "pinned": "",
+ "private": "",
+ "text": "",
+ "types": {
+ "customer": "",
+ "general": "",
+ "office": "",
+ "paint": "",
+ "parts": "",
+ "shop": "",
+ "supplement": ""
+ }
+ },
+ "labels": {
+ "newnoteplaceholder": ""
+ }
}
},
"production": {