Note insert.
This commit is contained in:
@@ -126,6 +126,48 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>actions</name>
|
<name>actions</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>add</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>cancel</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>signout</name>
|
<name>signout</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -253,6 +295,32 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>jobdetail</name>
|
<name>jobdetail</name>
|
||||||
<children>
|
<children>
|
||||||
|
<folder_node>
|
||||||
|
<name>actions</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>addnote</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>labels</name>
|
<name>labels</name>
|
||||||
<children>
|
<children>
|
||||||
@@ -4883,6 +4951,278 @@
|
|||||||
</folder_node>
|
</folder_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>notes</name>
|
||||||
|
<children>
|
||||||
|
<folder_node>
|
||||||
|
<name>fields</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>critical</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>pinned</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>private</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>text</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>types</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>customer</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>general</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>office</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>paint</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>parts</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>shop</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>supplement</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>labels</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>newnoteplaceholder</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
|
|||||||
@@ -3,14 +3,18 @@ import { useQuery } from "@apollo/client";
|
|||||||
import { AntDesign } from "@expo/vector-icons";
|
import { AntDesign } from "@expo/vector-icons";
|
||||||
import { useGlobalSearchParams } from "expo-router";
|
import { useGlobalSearchParams } from "expo-router";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
import React from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { FlatList, RefreshControl, View } from "react-native";
|
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 ErrorDisplay from "../error/error-display";
|
||||||
|
import NewNoteModal from "./new-note-modal";
|
||||||
|
|
||||||
export default function JobNotes() {
|
export default function JobNotes() {
|
||||||
const { jobId } = useGlobalSearchParams();
|
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, {
|
const { loading, error, data, refetch } = useQuery(GET_JOB_BY_PK, {
|
||||||
variables: {
|
variables: {
|
||||||
@@ -19,6 +23,11 @@ export default function JobNotes() {
|
|||||||
skip: !jobId,
|
skip: !jobId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleNoteCreated = useCallback(() => {
|
||||||
|
hideNoteModal();
|
||||||
|
refetch();
|
||||||
|
}, [refetch]);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const onRefresh = async () => {
|
const onRefresh = async () => {
|
||||||
return refetch();
|
return refetch();
|
||||||
@@ -36,16 +45,23 @@ export default function JobNotes() {
|
|||||||
}
|
}
|
||||||
const job = data.jobs_by_pk;
|
const job = data.jobs_by_pk;
|
||||||
|
|
||||||
if (job.notes.length === 0)
|
|
||||||
return (
|
return (
|
||||||
|
<View style={{ flex: 1, display: "flex" }}>
|
||||||
|
<Button
|
||||||
|
icon="plus"
|
||||||
|
mode="outlined"
|
||||||
|
onPress={showNoteModal}
|
||||||
|
style={{ margin: 8 }}
|
||||||
|
>
|
||||||
|
{t("jobdetail.actions.addnote")}
|
||||||
|
</Button>
|
||||||
|
{job.notes.length === 0 ? (
|
||||||
<Card>
|
<Card>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<Text>{t("jobdetail.labels.nojobnotes")}</Text>
|
<Text>{t("jobdetail.labels.nojobnotes")}</Text>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
) : (
|
||||||
|
|
||||||
return (
|
|
||||||
<FlatList
|
<FlatList
|
||||||
refreshControl={
|
refreshControl={
|
||||||
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
||||||
@@ -54,6 +70,15 @@ export default function JobNotes() {
|
|||||||
data={job.notes}
|
data={job.notes}
|
||||||
renderItem={(object) => <NoteListItem item={object.item} />}
|
renderItem={(object) => <NoteListItem item={object.item} />}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
<NewNoteModal
|
||||||
|
jobId={jobId}
|
||||||
|
visible={noteModalVisible}
|
||||||
|
onDismiss={hideNoteModal}
|
||||||
|
onCreated={handleNoteCreated}
|
||||||
|
relatedRos={[]} // TODO: supply relatedRos list
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +97,7 @@ function NoteListItem({ item }) {
|
|||||||
>
|
>
|
||||||
{item.private && (
|
{item.private && (
|
||||||
<AntDesign
|
<AntDesign
|
||||||
name="eyeo"
|
name="eye-invisible"
|
||||||
style={{ margin: 4 }}
|
style={{ margin: 4 }}
|
||||||
size={24}
|
size={24}
|
||||||
color="black"
|
color="black"
|
||||||
|
|||||||
370
components/job-notes/new-note-modal.jsx
Normal file
370
components/job-notes/new-note-modal.jsx
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
import { GET_JOB_BY_PK } from "@/graphql/jobs.queries";
|
||||||
|
import { INSERT_NEW_NOTE } from "@/graphql/notes.queries";
|
||||||
|
import { selectCurrentUser } from "@/redux/user/user.selectors";
|
||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Formik } from "formik";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import {
|
||||||
|
Keyboard,
|
||||||
|
KeyboardAvoidingView,
|
||||||
|
LayoutAnimation,
|
||||||
|
Platform,
|
||||||
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
|
UIManager,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Chip,
|
||||||
|
Divider,
|
||||||
|
HelperText,
|
||||||
|
Modal,
|
||||||
|
Portal,
|
||||||
|
Switch,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
useTheme,
|
||||||
|
} from "react-native-paper";
|
||||||
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NewNoteModal contract
|
||||||
|
* Props:
|
||||||
|
* - jobId: uuid string
|
||||||
|
* - visible: boolean - controls modal visibility
|
||||||
|
* - onDismiss: () => 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 (
|
||||||
|
<KeyboardAvoidingView
|
||||||
|
enabled={visible}
|
||||||
|
behavior={Platform.OS === "ios" ? "margin" : undefined}
|
||||||
|
keyboardVerticalOffset={Platform.select({
|
||||||
|
ios: insets.top + 60,
|
||||||
|
android: 0,
|
||||||
|
})}
|
||||||
|
style={(styles.kbWrapper, { height: !visible && 0 })} //Needed to fix flexing on parent component.
|
||||||
|
>
|
||||||
|
<Portal>
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
onDismiss={onDismiss}
|
||||||
|
contentContainerStyle={[
|
||||||
|
{ backgroundColor: theme.colors.surface },
|
||||||
|
styles.modalContainer,
|
||||||
|
{
|
||||||
|
marginBottom: keyboardSpace + insets.bottom,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Formik
|
||||||
|
initialValues={initialValues}
|
||||||
|
validate={(values) => {
|
||||||
|
const errors = {};
|
||||||
|
if (!values.text || !values.text.trim()) {
|
||||||
|
errors.text = t("general.validation.required");
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
{({
|
||||||
|
values,
|
||||||
|
errors,
|
||||||
|
touched,
|
||||||
|
setFieldValue,
|
||||||
|
handleSubmit,
|
||||||
|
status,
|
||||||
|
}) => (
|
||||||
|
<ScrollView
|
||||||
|
style={styles.formScroll}
|
||||||
|
contentContainerStyle={[styles.scrollContent]}
|
||||||
|
keyboardShouldPersistTaps="handled"
|
||||||
|
>
|
||||||
|
<Text variant="titleMedium" style={styles.modalTitle}>
|
||||||
|
{existingNote
|
||||||
|
? t("objects.notes.labels.editnote")
|
||||||
|
: t("jobdetail.actions.addnote")}
|
||||||
|
</Text>
|
||||||
|
<Divider style={{ marginBottom: 12 }} />
|
||||||
|
<View style={styles.rowWrap}>
|
||||||
|
<SwitchWithLabel
|
||||||
|
label={t("objects.notes.fields.critical")}
|
||||||
|
value={values.critical}
|
||||||
|
onValueChange={(v) => setFieldValue("critical", v)}
|
||||||
|
/>
|
||||||
|
<SwitchWithLabel
|
||||||
|
label={t("objects.notes.fields.private")}
|
||||||
|
value={values.private}
|
||||||
|
onValueChange={(v) => setFieldValue("private", v)}
|
||||||
|
/>
|
||||||
|
<SwitchWithLabel
|
||||||
|
label={t("objects.notes.fields.pinned")}
|
||||||
|
value={values.pinned}
|
||||||
|
onValueChange={(v) => setFieldValue("pinned", v)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.typeChips}>
|
||||||
|
{[
|
||||||
|
"general",
|
||||||
|
"customer",
|
||||||
|
"shop",
|
||||||
|
"office",
|
||||||
|
"parts",
|
||||||
|
"paint",
|
||||||
|
"supplement",
|
||||||
|
].map((option) => (
|
||||||
|
<Chip
|
||||||
|
key={option}
|
||||||
|
selected={values.type === option}
|
||||||
|
onPress={() => setFieldValue("type", option)}
|
||||||
|
style={styles.chip}
|
||||||
|
>
|
||||||
|
{t(`objects.notes.fields.types.${option}`)}
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
<TextInput
|
||||||
|
label={t("objects.notes.fields.text")}
|
||||||
|
value={values.text}
|
||||||
|
onChangeText={(v) => setFieldValue("text", v)}
|
||||||
|
multiline
|
||||||
|
mode="outlined"
|
||||||
|
placeholder={t("objects.notes.labels.newnoteplaceholder")}
|
||||||
|
style={styles.textArea}
|
||||||
|
/>
|
||||||
|
{touched.text && errors.text && (
|
||||||
|
<HelperText type="error" visible>
|
||||||
|
{errors.text}
|
||||||
|
</HelperText>
|
||||||
|
)}
|
||||||
|
{status?.error && (
|
||||||
|
<HelperText type="error" visible>
|
||||||
|
{status.error}
|
||||||
|
</HelperText>
|
||||||
|
)}
|
||||||
|
{!existingNote && filteredRelatedRos.length > 0 && (
|
||||||
|
<View style={styles.relatedRosContainer}>
|
||||||
|
<Text style={styles.relatedRosLabel}>
|
||||||
|
{t("objects.notes.labels.addtorelatedro")}
|
||||||
|
</Text>
|
||||||
|
{filteredRelatedRos.map((j) => (
|
||||||
|
<SwitchWithLabel
|
||||||
|
key={j.id}
|
||||||
|
label={`${j.ro_number || "N/A"}${
|
||||||
|
j.clm_no ? ` | ${j.clm_no}` : ""
|
||||||
|
}${j.status ? ` | ${j.status}` : ""}`}
|
||||||
|
value={values.relatedros?.[j.id] || false}
|
||||||
|
onValueChange={(v) =>
|
||||||
|
setFieldValue(`relatedros.${j.id}`, v)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.actions,
|
||||||
|
{ paddingBottom: Math.max(insets.bottom, 12) },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Button onPress={onDismiss} mode="text">
|
||||||
|
{t("general.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onPress={handleSubmit}
|
||||||
|
mode="contained"
|
||||||
|
loading={loading}
|
||||||
|
disabled={loading || !values.text.trim()}
|
||||||
|
>
|
||||||
|
{existingNote
|
||||||
|
? t("general.actions.save")
|
||||||
|
: t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</Modal>
|
||||||
|
</Portal>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SwitchWithLabel = ({ label, value, onValueChange }) => (
|
||||||
|
<View style={styles.checkboxItem}>
|
||||||
|
<Switch value={value} onValueChange={onValueChange} />
|
||||||
|
<Text style={styles.checkboxLabel}>{label}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
@@ -14,25 +14,7 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from "react-native-paper";
|
} from "react-native-paper";
|
||||||
|
|
||||||
/**
|
export const JobStatusSelector = ({
|
||||||
* 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<JobStatusSelectorProps> = ({
|
|
||||||
statuses,
|
statuses,
|
||||||
currentStatus,
|
currentStatus,
|
||||||
onSelect,
|
onSelect,
|
||||||
@@ -49,7 +31,7 @@ export const JobStatusSelector: React.FC<JobStatusSelectorProps> = ({
|
|||||||
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS);
|
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS);
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
async (status: string) => {
|
async (status) => {
|
||||||
Haptics.selectionAsync().catch(() => {});
|
Haptics.selectionAsync().catch(() => {});
|
||||||
hide();
|
hide();
|
||||||
await updateJobStatus({
|
await updateJobStatus({
|
||||||
@@ -89,7 +71,7 @@ export const JobStatusSelector: React.FC<JobStatusSelectorProps> = ({
|
|||||||
<Divider />
|
<Divider />
|
||||||
<FlatList
|
<FlatList
|
||||||
data={statuses}
|
data={statuses}
|
||||||
keyExtractor={keyExtractor}
|
keyExtractor={(item) => item}
|
||||||
renderItem={({ item }) => {
|
renderItem={({ item }) => {
|
||||||
const selected = item === currentStatus;
|
const selected = item === currentStatus;
|
||||||
return (
|
return (
|
||||||
@@ -155,12 +137,3 @@ const styles = StyleSheet.create({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default JobStatusSelector;
|
export default JobStatusSelector;
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage example:
|
|
||||||
* <JobStatusSelector
|
|
||||||
* statuses={availableStatuses}
|
|
||||||
* currentStatus={job.status}
|
|
||||||
* onSelect={(newStatus) => console.log('Status changed to', newStatus)}
|
|
||||||
* />
|
|
||||||
*/
|
|
||||||
@@ -5,17 +5,12 @@ import { useLocalSearchParams } from "expo-router";
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { RefreshControl, ScrollView, StyleSheet, View } from "react-native";
|
import { RefreshControl, ScrollView, StyleSheet, View } from "react-native";
|
||||||
import {
|
import { ActivityIndicator, Card, Chip, Text } from "react-native-paper";
|
||||||
ActivityIndicator,
|
|
||||||
Card,
|
|
||||||
Chip,
|
|
||||||
Text,
|
|
||||||
useTheme,
|
|
||||||
} from "react-native-paper";
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import DataLabelComponent from "../data-label/data-label";
|
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({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -31,9 +26,6 @@ function JobTombstone({ bodyshop }) {
|
|||||||
},
|
},
|
||||||
skip: !jobId,
|
skip: !jobId,
|
||||||
});
|
});
|
||||||
console.log("JobTombstone render");
|
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const onRefresh = async () => {
|
const onRefresh = async () => {
|
||||||
@@ -80,6 +72,11 @@ function JobTombstone({ bodyshop }) {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return <ActivityIndicator size="large" style={{ flex: 1 }} />;
|
return <ActivityIndicator size="large" style={{ flex: 1 }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <ErrorDisplay message={JSON.stringify(error)} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (!data.jobs_by_pk) {
|
if (!data.jobs_by_pk) {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
|
|||||||
22
graphql/notes.queries.js
Normal file
22
graphql/notes.queries.js
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
42
package-lock.json
generated
42
package-lock.json
generated
@@ -19,7 +19,7 @@
|
|||||||
"@reduxjs/toolkit": "^2.9.1",
|
"@reduxjs/toolkit": "^2.9.1",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"dinero.js": "^1.9.1",
|
"dinero.js": "^1.9.1",
|
||||||
"expo": "54.0.20",
|
"expo": "54.0.21",
|
||||||
"expo-application": "~7.0.7",
|
"expo-application": "~7.0.7",
|
||||||
"expo-constants": "~18.0.10",
|
"expo-constants": "~18.0.10",
|
||||||
"expo-dev-client": "~6.0.16",
|
"expo-dev-client": "~6.0.16",
|
||||||
@@ -2305,9 +2305,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@expo/metro-config": {
|
"node_modules/@expo/metro-config": {
|
||||||
"version": "54.0.7",
|
"version": "54.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-54.0.8.tgz",
|
||||||
"integrity": "sha512-bXluEygLrd7cIh/erpjIIC2xDeanaebcwzF+DUMD5vAqHU3o0QXAF3jRV/LsjXZud9V5eRpyCRZ3tLQL0iv8WA==",
|
"integrity": "sha512-rCkDQ8IT6sgcGNy48O2cTE4NlazCAgAIsD5qBsNPJLZSS0XbaILvAgGsFt/4nrx0GMGj6iQcOn5ifwV4NssTmw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.20.0",
|
"@babel/code-frame": "^7.20.0",
|
||||||
@@ -8184,19 +8184,19 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/expo": {
|
"node_modules/expo": {
|
||||||
"version": "54.0.20",
|
"version": "54.0.21",
|
||||||
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.20.tgz",
|
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.21.tgz",
|
||||||
"integrity": "sha512-mWHky+H63W60P5Oo+VbtqzF2sLvdaoSSwG57H9rlq1DrgIla++QJZuwJkXXo55lYPymVmkVhwG6FjWYKKylwpw==",
|
"integrity": "sha512-I3kzMNW/43a71pt6hT0Zebd2zAPIMMeucUDDEdfUKYrzzTRwISZfVAv0dp8GWKHHDjZsy+FjE4RQCMdyKmiDeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.0",
|
"@babel/runtime": "^7.20.0",
|
||||||
"@expo/cli": "54.0.13",
|
"@expo/cli": "54.0.14",
|
||||||
"@expo/config": "~12.0.10",
|
"@expo/config": "~12.0.10",
|
||||||
"@expo/config-plugins": "~54.0.2",
|
"@expo/config-plugins": "~54.0.2",
|
||||||
"@expo/devtools": "0.1.7",
|
"@expo/devtools": "0.1.7",
|
||||||
"@expo/fingerprint": "0.15.2",
|
"@expo/fingerprint": "0.15.2",
|
||||||
"@expo/metro": "~54.1.0",
|
"@expo/metro": "~54.1.0",
|
||||||
"@expo/metro-config": "54.0.7",
|
"@expo/metro-config": "54.0.8",
|
||||||
"@expo/vector-icons": "^15.0.3",
|
"@expo/vector-icons": "^15.0.3",
|
||||||
"@ungap/structured-clone": "^1.3.0",
|
"@ungap/structured-clone": "^1.3.0",
|
||||||
"babel-preset-expo": "~54.0.6",
|
"babel-preset-expo": "~54.0.6",
|
||||||
@@ -8206,7 +8206,7 @@
|
|||||||
"expo-font": "~14.0.9",
|
"expo-font": "~14.0.9",
|
||||||
"expo-keep-awake": "~15.0.7",
|
"expo-keep-awake": "~15.0.7",
|
||||||
"expo-modules-autolinking": "3.0.19",
|
"expo-modules-autolinking": "3.0.19",
|
||||||
"expo-modules-core": "3.0.22",
|
"expo-modules-core": "3.0.23",
|
||||||
"pretty-format": "^29.7.0",
|
"pretty-format": "^29.7.0",
|
||||||
"react-refresh": "^0.14.2",
|
"react-refresh": "^0.14.2",
|
||||||
"whatwg-url-without-unicode": "8.0.0-3"
|
"whatwg-url-without-unicode": "8.0.0-3"
|
||||||
@@ -8516,9 +8516,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo-modules-core": {
|
"node_modules/expo-modules-core": {
|
||||||
"version": "3.0.22",
|
"version": "3.0.23",
|
||||||
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.22.tgz",
|
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-3.0.23.tgz",
|
||||||
"integrity": "sha512-FqG5oelITFTLcIfGwoJP8Qsk65be/eiEjz354NdAurnhFARHAVYOOIsUehArvm75ISdZOIZEaTSjCudmkA3kKg==",
|
"integrity": "sha512-NYHi5LK/cdIyOjK9ZQAgfDPCOqER26cIbU3gzsce7YdnsmlNFR0qMfWOj9zAmaFBviC2kCkCOmitwk4357Td3Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"invariant": "^2.2.4"
|
"invariant": "^2.2.4"
|
||||||
@@ -8638,9 +8638,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo-server": {
|
"node_modules/expo-server": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.3.tgz",
|
||||||
"integrity": "sha512-QlQLjFuwgCiBc+Qq0IyBBHiZK1RS0NJSsKVB5iECMJrR04q7PhkaF7dON0fhvo00COy4fT9rJ5brrJDpFro/gA==",
|
"integrity": "sha512-SOwdzM/BFAL+vTFlUDJG6ljhyk6TyTl+LRK3ubGmN+Pf18ENRqKj37U8krc5vH926sAsB3IFcE8kJEYf4dG7PA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.16.0"
|
"node": ">=20.16.0"
|
||||||
@@ -8786,9 +8786,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expo/node_modules/@expo/cli": {
|
"node_modules/expo/node_modules/@expo/cli": {
|
||||||
"version": "54.0.13",
|
"version": "54.0.14",
|
||||||
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.14.tgz",
|
||||||
"integrity": "sha512-wUJVTByZzDN0q8UjXDlu6WD2BWoTJCKVVBGUBNmvViDX4FhnESwefmtXPoO54QUUKs6vY89WZryHllGArGfLLw==",
|
"integrity": "sha512-M7QW/GHx1FJg+CGgChGKerYXmCGWDskJ8S6w+8m49IBZ41CMDeWRH5snQkFoGCttF8WnzhGiX+nu69AFnEuDHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0no-co/graphql.web": "^1.0.8",
|
"@0no-co/graphql.web": "^1.0.8",
|
||||||
@@ -8801,7 +8801,7 @@
|
|||||||
"@expo/json-file": "^10.0.7",
|
"@expo/json-file": "^10.0.7",
|
||||||
"@expo/mcp-tunnel": "~0.0.7",
|
"@expo/mcp-tunnel": "~0.0.7",
|
||||||
"@expo/metro": "~54.1.0",
|
"@expo/metro": "~54.1.0",
|
||||||
"@expo/metro-config": "~54.0.7",
|
"@expo/metro-config": "~54.0.8",
|
||||||
"@expo/osascript": "^2.3.7",
|
"@expo/osascript": "^2.3.7",
|
||||||
"@expo/package-manager": "^1.9.8",
|
"@expo/package-manager": "^1.9.8",
|
||||||
"@expo/plist": "^0.4.7",
|
"@expo/plist": "^0.4.7",
|
||||||
@@ -8824,7 +8824,7 @@
|
|||||||
"connect": "^3.7.0",
|
"connect": "^3.7.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"env-editor": "^0.4.1",
|
"env-editor": "^0.4.1",
|
||||||
"expo-server": "^1.0.2",
|
"expo-server": "^1.0.3",
|
||||||
"freeport-async": "^2.0.0",
|
"freeport-async": "^2.0.0",
|
||||||
"getenv": "^2.0.0",
|
"getenv": "^2.0.0",
|
||||||
"glob": "^10.4.2",
|
"glob": "^10.4.2",
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"actions": {
|
"actions": {
|
||||||
|
"add": "Add",
|
||||||
|
"cancel": "Cancel",
|
||||||
"signout": "Sign Out"
|
"signout": "Sign Out"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -26,6 +28,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jobdetail": {
|
"jobdetail": {
|
||||||
|
"actions": {
|
||||||
|
"addnote": "Add Note"
|
||||||
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"claiminformation": "Claim Information",
|
"claiminformation": "Claim Information",
|
||||||
"dates": "Dates",
|
"dates": "Dates",
|
||||||
@@ -286,6 +291,26 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"inproduction": "In Production"
|
"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": {
|
"production": {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"actions": {
|
"actions": {
|
||||||
|
"add": "",
|
||||||
|
"cancel": "",
|
||||||
"signout": ""
|
"signout": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -26,6 +28,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jobdetail": {
|
"jobdetail": {
|
||||||
|
"actions": {
|
||||||
|
"addnote": ""
|
||||||
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"claiminformation": "",
|
"claiminformation": "",
|
||||||
"dates": "",
|
"dates": "",
|
||||||
@@ -286,6 +291,26 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"inproduction": ""
|
"inproduction": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"fields": {
|
||||||
|
"critical": "",
|
||||||
|
"pinned": "",
|
||||||
|
"private": "",
|
||||||
|
"text": "",
|
||||||
|
"types": {
|
||||||
|
"customer": "",
|
||||||
|
"general": "",
|
||||||
|
"office": "",
|
||||||
|
"paint": "",
|
||||||
|
"parts": "",
|
||||||
|
"shop": "",
|
||||||
|
"supplement": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"newnoteplaceholder": ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"actions": {
|
"actions": {
|
||||||
|
"add": "",
|
||||||
|
"cancel": "",
|
||||||
"signout": ""
|
"signout": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -26,6 +28,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jobdetail": {
|
"jobdetail": {
|
||||||
|
"actions": {
|
||||||
|
"addnote": ""
|
||||||
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"claiminformation": "",
|
"claiminformation": "",
|
||||||
"dates": "",
|
"dates": "",
|
||||||
@@ -286,6 +291,26 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"inproduction": ""
|
"inproduction": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"fields": {
|
||||||
|
"critical": "",
|
||||||
|
"pinned": "",
|
||||||
|
"private": "",
|
||||||
|
"text": "",
|
||||||
|
"types": {
|
||||||
|
"customer": "",
|
||||||
|
"general": "",
|
||||||
|
"office": "",
|
||||||
|
"paint": "",
|
||||||
|
"parts": "",
|
||||||
|
"shop": "",
|
||||||
|
"supplement": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"newnoteplaceholder": ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
|
|||||||
Reference in New Issue
Block a user