diff --git a/apps/mobile/app/components/Accordion.tsx b/apps/mobile/app/components/Accordion.tsx
new file mode 100644
index 000000000..34ca4a382
--- /dev/null
+++ b/apps/mobile/app/components/Accordion.tsx
@@ -0,0 +1,60 @@
+/* eslint-disable react-native/no-color-literals */
+/* eslint-disable react-native/no-inline-styles */
+import { View, Text, StyleSheet, TouchableOpacity } from "react-native"
+import React, { useState } from "react"
+import { Feather } from "@expo/vector-icons"
+import { useAppTheme } from "../theme"
+
+const Accordion = ({ children, title }) => {
+ const [expanded, setExpanded] = useState(true)
+ const { colors } = useAppTheme()
+
+ function toggleItem() {
+ setExpanded(!expanded)
+ }
+
+ const body = {children}
+ return (
+
+
+ {title}
+
+
+ {expanded && (
+
+
+
+ )}
+ {expanded && body}
+
+ )
+}
+
+export default Accordion
+
+const styles = StyleSheet.create({
+ accordContainer: {
+ borderRadius: 8,
+ width: "100%",
+ },
+ accordHeader: {
+ alignItems: "center",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ padding: 12,
+ },
+ accordTitle: {
+ fontSize: 20,
+ fontWeight: "600",
+ },
+})
diff --git a/apps/mobile/app/components/IssuesModal.tsx b/apps/mobile/app/components/IssuesModal.tsx
index b78ff7f42..654a4310c 100644
--- a/apps/mobile/app/components/IssuesModal.tsx
+++ b/apps/mobile/app/components/IssuesModal.tsx
@@ -18,20 +18,16 @@ import { SvgUri } from "react-native-svg"
import { IIssueType } from "../services/interfaces/ITaskIssue"
import { useTeamTasks } from "../services/hooks/features/useTeamTasks"
import { useAppTheme } from "../theme"
+import { BlurView } from "expo-blur"
interface IssuesModalProps {
task: ITeamTask
readonly?: boolean
nameIncluded?: boolean
- responsiveFontSize?: () => number
+ smallFont?: boolean
}
-const IssuesModal: FC = ({
- task,
- readonly = false,
- nameIncluded,
- responsiveFontSize,
-}) => {
+const IssuesModal: FC = ({ task, readonly = false, nameIncluded, smallFont }) => {
const { allTaskIssues } = useTaskIssue()
const [isModalOpen, setIsModalOpen] = useState(false)
const { updateTask } = useTeamTasks()
@@ -56,14 +52,14 @@ const IssuesModal: FC = ({
currentIssue?.name === "Bug" ? 15 : currentIssue?.name === "Story" ? 14 : 13
return (
- <>
+
= ({
{currentIssue?.name}
@@ -110,7 +106,7 @@ const IssuesModal: FC = ({
/>
- >
+
)
}
@@ -141,6 +137,15 @@ const ModalPopUp = ({ visible, children, onDismiss }) => {
}
return (
+
onDismiss()}>
@@ -185,7 +190,6 @@ const Item = ({ issue, onChangeIssue, closeModal, readonly = false }: IItem) =>
const $modalBackGround: ViewStyle = {
flex: 1,
- backgroundColor: "#000000AA",
justifyContent: "center",
}
diff --git a/apps/mobile/app/components/StatusType.tsx b/apps/mobile/app/components/StatusType.tsx
index 86002049d..f42ce589e 100644
--- a/apps/mobile/app/components/StatusType.tsx
+++ b/apps/mobile/app/components/StatusType.tsx
@@ -1,65 +1,80 @@
-import React, { useMemo } from 'react';
-import { View } from 'react-native';
-import { SvgUri } from 'react-native-svg';
-import { useTaskLabels } from '../services/hooks/features/useTaskLabels';
-import { useTaskPriority } from '../services/hooks/features/useTaskPriority';
-import { useTaskSizes } from '../services/hooks/features/useTaskSizes';
-import { useTaskStatus } from '../services/hooks/features/useTaskStatus';
-import { ITaskStatusItem } from '../services/interfaces/ITaskStatus';
+import React, { useMemo } from "react"
+import { View } from "react-native"
+import { SvgUri } from "react-native-svg"
+import { useTaskLabels } from "../services/hooks/features/useTaskLabels"
+import { useTaskPriority } from "../services/hooks/features/useTaskPriority"
+import { useTaskSizes } from "../services/hooks/features/useTaskSizes"
+import { useTaskStatus } from "../services/hooks/features/useTaskStatus"
+import { ITaskStatusItem } from "../services/interfaces/ITaskStatus"
+import { useTaskVersion } from "../services/hooks/features/useTaskVersion"
export type TStatusItem = {
- bgColor?: string;
- icon?: React.ReactNode | undefined;
- name?: string;
- value?: string;
- bordered?: boolean;
-};
+ bgColor?: string
+ icon?: React.ReactNode | undefined
+ name?: string
+ value?: string
+ bordered?: boolean
+}
export type TStatus = {
- [k in T]: TStatusItem;
-};
+ [k in T]: TStatusItem
+}
-export function useMapToTaskStatusValues(data: T[], bordered = false): TStatus {
+export function useMapToTaskStatusValues(
+ data: T[],
+ bordered = false,
+): TStatus {
return useMemo(() => {
return data.reduce((acc, item) => {
const value: TStatus[string] = {
- name: item.name?.split('-').join(' '),
+ name: item.name?.split("-").join(" "),
value: item.value || item.name,
bgColor: item.color,
bordered,
- icon: {item.fullIconUrl && }
- };
+ icon: (
+
+ {item.fullIconUrl && (
+
+ )}
+
+ ),
+ }
if (value.name) {
- acc[value.name] = value;
+ acc[value.name] = value
} else if (value.value) {
- acc[value.value] = value;
+ acc[value.value] = value
}
- return acc;
- }, {} as TStatus);
- }, [data, bordered]);
+ return acc
+ }, {} as TStatus)
+ }, [data, bordered])
}
// ==================== Task Status ========================================
export function useTaskStatusValue() {
- const { allStatuses } = useTaskStatus();
- return useMapToTaskStatusValues(allStatuses);
+ const { allStatuses } = useTaskStatus()
+ return useMapToTaskStatusValues(allStatuses)
}
// =================== Task Size ==============================================
export function useTaskSizeValue() {
- const { allTaskSizes } = useTaskSizes();
- return useMapToTaskStatusValues(allTaskSizes);
+ const { allTaskSizes } = useTaskSizes()
+ return useMapToTaskStatusValues(allTaskSizes)
}
// =================== Task Label ==============================================
export function useTaskLabelValue() {
- const { allTaskLabels } = useTaskLabels();
- return useMapToTaskStatusValues(allTaskLabels);
+ const { allTaskLabels } = useTaskLabels()
+ return useMapToTaskStatusValues(allTaskLabels)
}
// =================== Task Priority ==============================================
export function useTaskPriorityValue() {
- const { allTaskPriorities } = useTaskPriority();
- return useMapToTaskStatusValues(allTaskPriorities);
+ const { allTaskPriorities } = useTaskPriority()
+ return useMapToTaskStatusValues(allTaskPriorities)
+}
+// =================== Task Priority ==============================================
+export function useTaskVersionValue() {
+ const { taskVersionList } = useTaskVersion()
+ return useMapToTaskStatusValues(taskVersionList)
}
diff --git a/apps/mobile/app/components/Task/DetailsBlock/blocks/TaskMainInfo.tsx b/apps/mobile/app/components/Task/DetailsBlock/blocks/TaskMainInfo.tsx
new file mode 100644
index 000000000..2261c9811
--- /dev/null
+++ b/apps/mobile/app/components/Task/DetailsBlock/blocks/TaskMainInfo.tsx
@@ -0,0 +1,350 @@
+/* eslint-disable react-native/no-inline-styles */
+/* eslint-disable react-native/no-color-literals */
+import { View, Text, StyleSheet } from "react-native"
+import React from "react"
+import { useStores } from "../../../../models"
+import TaskRow from "../components/TaskRow"
+import { SvgXml } from "react-native-svg"
+import {
+ calendarIcon,
+ categoryIcon,
+ clipboardIcon,
+ peopleIconSmall,
+ profileIcon,
+} from "../../../svgs/icons"
+import ProfileInfo from "../components/ProfileInfo"
+import ManageAssignees from "../components/ManageAssignees"
+import { useOrganizationTeam } from "../../../../services/hooks/useOrganization"
+import CalendarModal from "../components/CalendarModal"
+import { useTeamTasks } from "../../../../services/hooks/features/useTeamTasks"
+import moment from "moment-timezone"
+import TaskStatus from "../../../TaskStatus"
+import TaskSize from "../../../TaskSize"
+import TaskPriority from "../../../TaskPriority"
+import TaskLabels from "../../../TaskLabels"
+import TaskVersion from "../../../TaskVersion"
+import { useAppTheme } from "../../../../theme"
+import { ITeamTask } from "../../../../services/interfaces/ITask"
+import { TouchableOpacity } from "react-native-gesture-handler"
+import { useNavigation } from "@react-navigation/native"
+import { SettingScreenNavigationProp } from "../../../../navigators/AuthenticatedNavigator"
+import TaskEpic from "../../../TaskEpic"
+import IssuesModal from "../../../IssuesModal"
+import { observer } from "mobx-react-lite"
+import { translate } from "../../../../i18n"
+
+const TaskMainInfo = observer(() => {
+ const {
+ TaskStore: { detailedTask: task },
+ } = useStores()
+
+ const { currentTeam } = useOrganizationTeam()
+ const { updateTask } = useTeamTasks()
+
+ const { colors } = useAppTheme()
+
+ return (
+
+ {/* Issue type */}
+
+
+
+ {translate("taskDetailsScreen.typeIssue")}
+
+
+ }
+ >
+
+
+ {/* Creator */}
+
+
+
+ {translate("taskDetailsScreen.creator")}
+
+
+ }
+ >
+
+
+ {/* Assignees */}
+
+
+
+ {translate("taskDetailsScreen.assignees")}
+
+
+ }
+ >
+ {task?.members?.map((member, index) => (
+
+ ))}
+
+ {/* Manage Assignees */}
+
+
+
+ {/* Manage Start Date */}
+
+
+
+ {translate("taskDetailsScreen.startDate")}
+
+
+ }
+ >
+ updateTask({ ...task, startDate: date }, task?.id)}
+ selectedDate={task?.startDate}
+ />
+
+
+ {/* Manage Due Date */}
+
+
+ {translate("taskDetailsScreen.dueDate")}
+
+
+ }
+ >
+ updateTask({ ...task, dueDate: date }, task?.id)}
+ selectedDate={task?.dueDate}
+ isDueDate={true}
+ />
+
+
+ {/* Days Remaining */}
+ {task?.startDate && task?.dueDate && (
+
+
+ {translate("taskDetailsScreen.daysRemaining")}
+
+
+ }
+ >
+
+ {moment(task?.dueDate).diff(moment(), "days") < 0
+ ? 0
+ : moment(task?.dueDate).diff(moment(), "days")}
+
+
+ )}
+
+ {/* horizontal separator */}
+
+
+ {/* Version */}
+
+
+ {translate("taskDetailsScreen.version")}
+
+
+ }
+ >
+
+
+
+ {/* Epic */}
+ {task && task.issueType === "Story" && (
+
+
+ {translate("taskDetailsScreen.epic")}
+
+
+ }
+ >
+
+
+ )}
+ {task && }
+
+ {/* Status */}
+
+
+ {translate("taskDetailsScreen.status")}
+
+
+ }
+ >
+
+
+
+ {/* Labels */}
+
+
+ {translate("taskDetailsScreen.labels")}
+
+
+ }
+ >
+
+
+
+ {/* Size */}
+
+ {translate("taskDetailsScreen.size")}
+
+ }
+ >
+
+
+
+ {/* Priority */}
+
+
+ {translate("taskDetailsScreen.priority")}
+
+
+ }
+ >
+
+
+
+ )
+})
+
+export default TaskMainInfo
+
+const EpicParent: React.FC<{ task: ITeamTask }> = ({ task }) => {
+ const { colors } = useAppTheme()
+
+ const navigation = useNavigation>()
+
+ const navigateToEpic = () => {
+ navigation.navigate("TaskScreen", { taskId: task?.rootEpic?.id })
+ }
+
+ if (task?.issueType === "Story") {
+ return <>>
+ }
+ return (!task?.issueType || task?.issueType === "Task" || task?.issueType === "Bug") &&
+ task?.rootEpic ? (
+
+ {translate("taskDetailsScreen.epic")}
+
+ }
+ >
+
+
+
+
+ {`#${task?.rootEpic?.number} ${task?.rootEpic?.title}`}
+
+
+ ) : (
+ <>>
+ )
+}
+
+const styles = StyleSheet.create({
+ epicParentButton: { alignItems: "center", flexDirection: "row", gap: 4, width: "60%" },
+ epicParentIconWrapper: {
+ backgroundColor: "#8154BA",
+ borderRadius: 4,
+ marginVertical: 5,
+ padding: 4,
+ },
+ horizontalSeparator: {
+ borderBottomColor: "#F2F2F2",
+ borderBottomWidth: 1,
+ marginVertical: 10,
+ width: "100%",
+ },
+ labelComponent: {
+ alignItems: "center",
+ flexDirection: "row",
+ gap: 7,
+ },
+ labelText: {
+ color: "#A5A2B2",
+ fontSize: 12,
+ },
+})
diff --git a/apps/mobile/app/components/Task/DetailsBlock/blocks/TaskPublicity.tsx b/apps/mobile/app/components/Task/DetailsBlock/blocks/TaskPublicity.tsx
new file mode 100644
index 000000000..98ee9060d
--- /dev/null
+++ b/apps/mobile/app/components/Task/DetailsBlock/blocks/TaskPublicity.tsx
@@ -0,0 +1,86 @@
+/* eslint-disable react-native/no-inline-styles */
+/* eslint-disable react-native/no-color-literals */
+import { View, Text, StyleSheet } from "react-native"
+import React, { useCallback, useEffect, useState } from "react"
+import { useStores } from "../../../../models"
+import { translate } from "../../../../i18n"
+import { SvgXml } from "react-native-svg"
+import { globeDarkTheme, globeLightTheme, lockDarkTheme, lockLightTheme } from "../../../svgs/icons"
+import { useAppTheme } from "../../../../theme"
+import { TouchableOpacity } from "react-native-gesture-handler"
+import { debounce } from "lodash"
+import { useTeamTasks } from "../../../../services/hooks/features/useTeamTasks"
+
+const TaskPublicity = () => {
+ const {
+ TaskStore: { detailedTask: task },
+ } = useStores()
+ const { updatePublicity } = useTeamTasks()
+
+ const { dark, colors } = useAppTheme()
+ const [isTaskPublic, setIsTaskPublic] = useState(task?.public)
+
+ const handlePublicity = useCallback(
+ (value: boolean) => {
+ setIsTaskPublic(value)
+ const debounceUpdatePublicity = debounce((value) => {
+ updatePublicity(value, task, true)
+ }, 500)
+ debounceUpdatePublicity(value)
+ },
+ [task, updatePublicity],
+ )
+
+ useEffect(() => {
+ setIsTaskPublic(task?.public)
+ }, [task?.public])
+
+ return (
+
+ {isTaskPublic ? (
+
+
+
+
+ {translate("taskDetailsScreen.taskPublic")}
+
+
+ handlePublicity(false)}>
+
+ {translate("taskDetailsScreen.makePrivate")}
+
+
+
+ ) : (
+
+
+
+
+ {translate("taskDetailsScreen.taskPrivate")}
+
+
+ handlePublicity(true)}>
+
+ {translate("taskDetailsScreen.makePublic")}
+
+
+
+ )}
+
+ )
+}
+
+export default TaskPublicity
+
+const styles = StyleSheet.create({
+ taskPrivacyWrapper: {
+ alignItems: "center",
+ flexDirection: "row",
+ gap: 8,
+ },
+ wrapper: {
+ alignItems: "center",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+})
diff --git a/apps/mobile/app/components/Task/DetailsBlock/components/CalendarModal.tsx b/apps/mobile/app/components/Task/DetailsBlock/components/CalendarModal.tsx
new file mode 100644
index 000000000..974f46be6
--- /dev/null
+++ b/apps/mobile/app/components/Task/DetailsBlock/components/CalendarModal.tsx
@@ -0,0 +1,155 @@
+/* eslint-disable react-native/no-inline-styles */
+import {
+ View,
+ Text,
+ Animated,
+ Modal,
+ TouchableWithoutFeedback,
+ TouchableOpacity,
+ ViewStyle,
+ StyleSheet,
+} from "react-native"
+import React, { useEffect, useState } from "react"
+import { useAppTheme } from "../../../../theme"
+import { Calendar } from "react-native-calendars"
+import moment from "moment-timezone"
+import { SvgXml } from "react-native-svg"
+import { trashIconSmall } from "../../../svgs/icons"
+import { BlurView } from "expo-blur"
+import { translate } from "../../../../i18n"
+
+interface ICalendarModal {
+ selectedDate: string
+ isDueDate?: boolean
+ updateTask: (date: string) => void
+}
+
+const CalendarModal: React.FC = ({ selectedDate, isDueDate, updateTask }) => {
+ const { colors } = useAppTheme()
+ const [modalVisible, setModalVisible] = useState(false)
+ const [selected, setSelected] = useState("")
+
+ useEffect(() => {
+ selectedDate && setSelected(moment(selectedDate).format("YYYY-MM-DD"))
+ }, [selectedDate])
+
+ const formatted = moment(selected).format("DD MMM YYYY")
+
+ return (
+
+
+ setModalVisible(true)}>
+
+ {selected
+ ? formatted
+ : isDueDate
+ ? translate("taskDetailsScreen.setDueDate")
+ : translate("taskDetailsScreen.setStartDate")}
+
+
+
+ {selected && selectedDate && (
+ {
+ setSelected("")
+ updateTask(null)
+ }}
+ >
+
+
+ )}
+
+
+ setModalVisible(false)}>
+
+ {
+ setSelected(day.dateString)
+ updateTask(moment(day.dateString).toISOString())
+ }}
+ markedDates={{
+ [selected]: {
+ selected: true,
+ disableTouchEvent: true,
+ selectedColor: colors.secondary,
+ dotColor: "orange",
+ },
+ }}
+ theme={{
+ todayTextColor: colors.secondary,
+ arrowColor: colors.secondary,
+ }}
+ />
+
+
+
+ )
+}
+
+export default CalendarModal
+
+const ModalPopUp = ({ visible, children, onDismiss }) => {
+ const [showModal, setShowModal] = React.useState(visible)
+ const scaleValue = React.useRef(new Animated.Value(0)).current
+
+ React.useEffect(() => {
+ toggleModal()
+ }, [visible])
+ const toggleModal = () => {
+ if (visible) {
+ setShowModal(true)
+ Animated.spring(scaleValue, {
+ toValue: 1,
+ useNativeDriver: true,
+ }).start()
+ } else {
+ setTimeout(() => setShowModal(false), 200)
+ Animated.timing(scaleValue, {
+ toValue: 0,
+ duration: 300,
+ useNativeDriver: true,
+ }).start()
+ }
+ }
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+ )
+}
+
+const $modalBackGround: ViewStyle = {
+ flex: 1,
+ justifyContent: "center",
+}
+
+const styles = StyleSheet.create({
+ buttonWrapper: {
+ alignItems: "center",
+ flexDirection: "row",
+ height: 14,
+ justifyContent: "space-between",
+ width: 110,
+ },
+ container: {
+ alignSelf: "center",
+ borderRadius: 20,
+ padding: 20,
+ width: "90%",
+ },
+})
diff --git a/apps/mobile/app/components/Task/DetailsBlock/components/ManageAssignees.tsx b/apps/mobile/app/components/Task/DetailsBlock/components/ManageAssignees.tsx
new file mode 100644
index 000000000..d93fc4fc2
--- /dev/null
+++ b/apps/mobile/app/components/Task/DetailsBlock/components/ManageAssignees.tsx
@@ -0,0 +1,233 @@
+/* eslint-disable react-native/no-color-literals */
+/* eslint-disable react-native/no-inline-styles */
+/* eslint-disable camelcase */
+import {
+ View,
+ Text,
+ Modal,
+ TouchableWithoutFeedback,
+ Animated,
+ ViewStyle,
+ StyleSheet,
+ TouchableOpacity,
+ Dimensions,
+} from "react-native"
+import React, { useEffect, useMemo, useState } from "react"
+import { useAppTheme } from "../../../../theme"
+import { OT_Member } from "../../../../services/interfaces/IOrganizationTeam"
+import ProfileInfo from "./ProfileInfo"
+import { SvgXml } from "react-native-svg"
+import { trashIconLarge } from "../../../svgs/icons"
+import { ITeamTask } from "../../../../services/interfaces/ITask"
+import { useTeamMemberCard } from "../../../../services/hooks/features/useTeamMemberCard"
+import { ScrollView } from "react-native-gesture-handler"
+import { BlurView } from "expo-blur"
+import { translate } from "../../../../i18n"
+
+interface IManageAssignees {
+ memberList: OT_Member[]
+ task: ITeamTask
+}
+
+const ManageAssignees: React.FC = ({ memberList, task }) => {
+ const [modalVisible, setModalVisible] = useState(false)
+ const [member, setMember] = useState()
+ const [memberToRemove, setMemberToRemove] = useState(false)
+ const [memberToAdd, setMemberToAdd] = useState(false)
+
+ const { colors } = useAppTheme()
+ const memberInfo = useTeamMemberCard(member)
+ const { height } = Dimensions.get("window")
+
+ const assignedToTaskMembers = useMemo(
+ () =>
+ memberList?.filter((member) =>
+ member.employee
+ ? task?.members.map((item) => item.userId).includes(member.employee?.userId)
+ : false,
+ ),
+ [memberList, task?.members],
+ )
+
+ const unassignedMembers = useMemo(
+ () =>
+ memberList?.filter((member) =>
+ member.employee
+ ? !task?.members.map((item) => item.userId).includes(member.employee.userId)
+ : false,
+ ),
+ [memberList, task?.members],
+ )
+
+ useEffect(() => {
+ if (task && member && memberToRemove) {
+ memberInfo
+ .unassignTask(task)
+ .then(() => {
+ setMember(undefined)
+ setMemberToRemove(false)
+ })
+ .catch(() => {
+ setMember(undefined)
+ setMemberToRemove(false)
+ })
+ } else if (task && member && memberToAdd) {
+ memberInfo
+ .assignTask(task)
+ .then(() => {
+ setMember(undefined)
+ setMemberToAdd(false)
+ })
+ .catch(() => {
+ setMember(undefined)
+ setMemberToAdd(false)
+ })
+ }
+ }, [task, member, memberInfo, memberToAdd, memberToRemove])
+
+ return (
+
+ setModalVisible(true)} style={styles.button}>
+
+ {translate("taskDetailsScreen.manageAssignees")}
+
+
+
+ setModalVisible(false)}>
+
+
+ {assignedToTaskMembers?.map((member, index) => (
+ {
+ setMember(member)
+ setMemberToRemove(true)
+ setModalVisible(false)
+ }}
+ key={index}
+ style={styles.memberWrapper}
+ >
+
+
+
+
+
+
+
+ ))}
+ {unassignedMembers?.map((member, index) => (
+ {
+ setMember(member)
+ setMemberToAdd(true)
+ setModalVisible(false)
+ }}
+ key={index}
+ style={styles.memberWrapper}
+ >
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
+
+export default ManageAssignees
+
+const ModalPopUp = ({ visible, children, onDismiss }) => {
+ const [showModal, setShowModal] = React.useState(visible)
+ const scaleValue = React.useRef(new Animated.Value(0)).current
+
+ React.useEffect(() => {
+ toggleModal()
+ }, [visible])
+ const toggleModal = () => {
+ if (visible) {
+ setShowModal(true)
+ Animated.spring(scaleValue, {
+ toValue: 1,
+ useNativeDriver: true,
+ }).start()
+ } else {
+ setTimeout(() => setShowModal(false), 200)
+ Animated.timing(scaleValue, {
+ toValue: 0,
+ duration: 300,
+ useNativeDriver: true,
+ }).start()
+ }
+ }
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+ )
+}
+
+const $modalBackGround: ViewStyle = {
+ flex: 1,
+ justifyContent: "center",
+}
+
+const styles = StyleSheet.create({
+ button: {
+ alignItems: "center",
+ borderColor: "#E5E7EB",
+ borderRadius: 100,
+ borderWidth: 1,
+ height: 24,
+ justifyContent: "center",
+ marginVertical: 10,
+ paddingHorizontal: 8,
+ paddingVertical: 4,
+ width: 140,
+ },
+ container: {
+ alignSelf: "center",
+ borderRadius: 20,
+ padding: 20,
+ width: "70%",
+ },
+ memberWrapper: {
+ alignItems: "center",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ marginBottom: 10,
+ },
+})
diff --git a/apps/mobile/app/components/Task/DetailsBlock/components/ProfileInfo.tsx b/apps/mobile/app/components/Task/DetailsBlock/components/ProfileInfo.tsx
new file mode 100644
index 000000000..a19c22bf2
--- /dev/null
+++ b/apps/mobile/app/components/Task/DetailsBlock/components/ProfileInfo.tsx
@@ -0,0 +1,84 @@
+/* eslint-disable react-native/no-inline-styles */
+/* eslint-disable react-native/no-color-literals */
+import { Text, StyleSheet, TouchableOpacity } from "react-native"
+import React from "react"
+import { Avatar } from "react-native-paper"
+import { imgTitleProfileAvatar } from "../../../../helpers/img-title-profile-avatar"
+import { typography, useAppTheme } from "../../../../theme"
+import { limitTextCharaters } from "../../../../helpers/sub-text"
+import { useNavigation } from "@react-navigation/native"
+import {
+ DrawerNavigationProp,
+ SettingScreenNavigationProp,
+} from "../../../../navigators/AuthenticatedNavigator"
+
+interface IProfileInfo {
+ names: string
+ profilePicSrc: string
+ userId?: string
+ largerProfileInfo?: boolean
+}
+
+const ProfileInfo: React.FC = ({
+ profilePicSrc,
+ names,
+ userId,
+ largerProfileInfo,
+}) => {
+ const { colors } = useAppTheme()
+
+ const alternateNavigation = useNavigation>()
+ const navigation = useNavigation>()
+
+ const navigateToProfile = () => {
+ alternateNavigation.navigate("AuthenticatedTab")
+ setTimeout(() => {
+ navigation.navigate("Profile", { userId, activeTab: "worked" })
+ }, 50)
+ }
+ return (
+
+ {profilePicSrc ? (
+
+ ) : (
+
+ )}
+
+
+ {limitTextCharaters({ text: names.trim(), numChars: 18 })}
+
+
+ )
+}
+
+export default ProfileInfo
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: "center",
+ flexDirection: "row",
+ gap: 7,
+ },
+ prefix: {
+ color: "#FFFFFF",
+ fontFamily: typography.fonts.PlusJakartaSans.light,
+ },
+ profileImage: {
+ borderRadius: 100,
+ },
+})
diff --git a/apps/mobile/app/components/Task/DetailsBlock/components/TaskRow.tsx b/apps/mobile/app/components/Task/DetailsBlock/components/TaskRow.tsx
new file mode 100644
index 000000000..f2576fa44
--- /dev/null
+++ b/apps/mobile/app/components/Task/DetailsBlock/components/TaskRow.tsx
@@ -0,0 +1,34 @@
+/* eslint-disable react-native/no-inline-styles */
+import { View, StyleSheet } from "react-native"
+import React from "react"
+
+interface ITaskRow {
+ labelComponent: React.ReactNode
+ children: React.ReactNode
+ alignItems?: boolean
+}
+
+const TaskRow = ({ labelComponent, children, alignItems }: ITaskRow) => {
+ return (
+
+ {labelComponent}
+ {children}
+
+ )
+}
+
+export default TaskRow
+
+const styles = StyleSheet.create({
+ childrenContainer: {
+ gap: 7,
+ width: "56%",
+ },
+ container: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ labelContainer: {
+ width: "40%",
+ },
+})
diff --git a/apps/mobile/app/components/Task/DetailsBlock/index.tsx b/apps/mobile/app/components/Task/DetailsBlock/index.tsx
new file mode 100644
index 000000000..a4b209f0a
--- /dev/null
+++ b/apps/mobile/app/components/Task/DetailsBlock/index.tsx
@@ -0,0 +1,16 @@
+import React from "react"
+import Accordion from "../../Accordion"
+import TaskPublicity from "./blocks/TaskPublicity"
+import TaskMainInfo from "./blocks/TaskMainInfo"
+import { translate } from "../../../i18n"
+
+const DetailsBlock = () => {
+ return (
+
+
+
+
+ )
+}
+
+export default DetailsBlock
diff --git a/apps/mobile/app/components/Task/TitleBlock/CreateParentTaskModal.tsx b/apps/mobile/app/components/Task/TitleBlock/CreateParentTaskModal.tsx
index 8356d53af..c3ffcc69d 100644
--- a/apps/mobile/app/components/Task/TitleBlock/CreateParentTaskModal.tsx
+++ b/apps/mobile/app/components/Task/TitleBlock/CreateParentTaskModal.tsx
@@ -21,6 +21,7 @@ import { useTaskInput } from "../../../services/hooks/features/useTaskInput"
import { typography, useAppTheme } from "../../../theme"
import { Feather } from "@expo/vector-icons"
import { ITeamTask } from "../../../services/interfaces/ITask"
+import { BlurView } from "expo-blur"
interface ICreateParentTaskModal {
visible: boolean
@@ -181,6 +182,15 @@ const ModalPopUp = ({ visible, children, onDismiss }) => {
}
return (
+
@@ -194,7 +204,6 @@ const ModalPopUp = ({ visible, children, onDismiss }) => {
const $modalBackGround: ViewStyle = {
flex: 1,
- backgroundColor: "#000000AA",
justifyContent: "center",
}
diff --git a/apps/mobile/app/components/Task/TitleBlock/index.tsx b/apps/mobile/app/components/Task/TitleBlock/index.tsx
index 2adf6a770..f073a4d3c 100644
--- a/apps/mobile/app/components/Task/TitleBlock/index.tsx
+++ b/apps/mobile/app/components/Task/TitleBlock/index.tsx
@@ -30,8 +30,6 @@ const TaskTitleBlock = observer(() => {
const { updateTitle } = useTeamTasks()
- const { width } = Dimensions.get("window")
-
const saveTitle = useCallback((newTitle: string) => {
if (newTitle.length > 255) {
showMessage({
@@ -60,22 +58,8 @@ const TaskTitleBlock = observer(() => {
})
}
- const responsiveFontSize = (): number => {
- const baseWidth = 428
- const scale = width / baseWidth
- const baseFontSize = 10
-
- const fontSize = Math.round(baseFontSize * scale)
-
- if (fontSize < 10) {
- return 10
- }
-
- return fontSize
- }
-
return (
-
+
{
#{task?.number}
-
+
{task?.issueType !== "Epic" && (
{
task?.rootEpic &&
task?.parentId !== task?.rootEpic.id && (
{
/>
)}
-
+
-
+
)
@@ -188,117 +167,111 @@ const TitleIcons: React.FC = ({ dark, edit, setEdit, copyTitle, sav
)
}
-const ParentTaskBadge: React.FC<{ task: ITeamTask; responsiveFontSize: () => number }> = observer(
- ({ task, responsiveFontSize }) => {
- const navigation = useNavigation>()
+const ParentTaskBadge: React.FC<{ task: ITeamTask }> = observer(({ task }) => {
+ const navigation = useNavigation>()
- const { width } = Dimensions.get("window")
+ const { width } = Dimensions.get("window")
- const navigateToParent = (): void => {
- navigation.navigate("TaskScreen", { taskId: task?.parentId || task?.parent.id })
- }
- return task?.parentId && task?.parent ? (
- {
+ navigation.navigate("TaskScreen", { taskId: task?.parentId || task?.parent.id })
+ }
+ return task?.parentId && task?.parent ? (
+
+
-
- #{task?.parent?.taskNumber || task?.parent.number}
-
- {` - ${limitTextCharaters({
- text: task?.parent?.title,
- numChars: width < 391 ? 8 : width <= 410 ? 12 : 18,
- })}`}
+ #{task?.parent?.taskNumber || task?.parent.number}
-
- ) : (
- <>>
- )
- },
-)
+ {` - ${limitTextCharaters({
+ text: task?.parent?.title,
+ numChars: width < 391 ? 8 : width <= 410 ? 12 : 18,
+ })}`}
+
+
+ ) : (
+ <>>
+ )
+})
-const ParentTaskInput: React.FC<{ task: ITeamTask; responsiveFontSize: () => number }> = observer(
- ({ task, responsiveFontSize }) => {
- const [modalVisible, setModalVisible] = useState(false)
- return task && task?.issueType !== "Epic" ? (
- setModalVisible(true)}
- >
-
- {task?.parentId
- ? translate("taskDetailsScreen.changeParent")
- : "+ " + translate("taskDetailsScreen.addParent")}
-
+const ParentTaskInput: React.FC<{ task: ITeamTask }> = observer(({ task }) => {
+ const [modalVisible, setModalVisible] = useState(false)
+ return task && task?.issueType !== "Epic" ? (
+ setModalVisible(true)}
+ >
+
+ {task?.parentId
+ ? translate("taskDetailsScreen.changeParent")
+ : "+ " + translate("taskDetailsScreen.addParent")}
+
- setModalVisible(false)}
- task={task}
- />
-
- ) : (
- <>>
- )
- },
-)
+ setModalVisible(false)}
+ task={task}
+ />
+
+ ) : (
+ <>>
+ )
+})
const styles = StyleSheet.create({
copyButton: {
diff --git a/apps/mobile/app/components/TaskEpic.tsx b/apps/mobile/app/components/TaskEpic.tsx
new file mode 100644
index 000000000..5ee6b7df9
--- /dev/null
+++ b/apps/mobile/app/components/TaskEpic.tsx
@@ -0,0 +1,164 @@
+/* eslint-disable react-native/no-color-literals */
+/* eslint-disable react-native/no-inline-styles */
+import { StyleSheet, Text, View, ViewStyle } from "react-native"
+import React, { useCallback, useMemo, useState } from "react"
+import { TouchableOpacity } from "react-native-gesture-handler"
+import { ITeamTask } from "../services/interfaces/ITask"
+import { typography, useAppTheme } from "../theme"
+import { useTeamTasks } from "../services/hooks/features/useTeamTasks"
+import { cloneDeep } from "lodash"
+import { SvgXml } from "react-native-svg"
+import { categoryIcon } from "./svgs/icons"
+import TaskEpicPopup from "./TaskEpicPopup"
+import { Entypo } from "@expo/vector-icons"
+import { translate } from "../i18n"
+
+interface ITaskEpic {
+ task: ITeamTask
+ containerStyle?: ViewStyle
+}
+
+export type formattedEpic = {
+ [key: string]: {
+ icon: React.ReactNode
+ id: string
+ name: string
+ value: string
+ }
+}
+
+const TaskEpic: React.FC = ({ containerStyle, task }) => {
+ const { colors, dark } = useAppTheme()
+ const { updateTask } = useTeamTasks()
+
+ const [openModal, setOpenModal] = useState(false)
+
+ const { teamTasks } = useTeamTasks()
+
+ const epicsList = useMemo(() => {
+ const temp: formattedEpic = {}
+ teamTasks.forEach((task) => {
+ if (task.issueType === "Epic") {
+ temp[`#${task.taskNumber} ${task.title}`] = {
+ id: task.id,
+ name: `#${task.taskNumber} ${task.title}`,
+ value: task.id,
+ icon: (
+
+
+
+ ),
+ }
+ }
+ })
+ return temp
+ }, [teamTasks])
+
+ const onTaskSelect = useCallback(
+ async (parentTask: ITeamTask | undefined) => {
+ if (!parentTask) return
+ const childTask = cloneDeep(task)
+
+ await updateTask(
+ {
+ ...childTask,
+ parentId: parentTask.id ? parentTask.id : null,
+ parent: parentTask.id ? parentTask : null,
+ },
+ task?.id,
+ )
+ },
+ [task, updateTask],
+ )
+ return (
+ <>
+ setOpenModal(false)}
+ onTaskSelect={onTaskSelect}
+ epicsList={epicsList}
+ currentEpic={task?.parent?.id}
+ teamTasks={teamTasks}
+ />
+
+ setOpenModal(true)}>
+
+ {task?.parent ? (
+ <>
+
+
+
+ {`#${task?.parent?.number} ${task?.parent?.title}`}
+ >
+ ) : (
+ <>
+
+
+ {translate("taskDetailsScreen.epic")}
+
+ >
+ )}
+
+
+ >
+ )
+}
+
+export default TaskEpic
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: "center",
+ borderColor: "rgba(0,0,0,0.16)",
+ borderRadius: 10,
+ flexDirection: "row",
+ gap: 3,
+ justifyContent: "space-between",
+ minHeight: 30,
+ minWidth: 100,
+ paddingHorizontal: 8,
+ },
+ epicParentIconWrapper: {
+ backgroundColor: "#8154BA",
+ borderRadius: 4,
+ marginVertical: 5,
+ padding: 4,
+ },
+ text: {
+ fontFamily: typography.fonts.PlusJakartaSans.semiBold,
+ fontSize: 10,
+ textTransform: "capitalize",
+ width: "90%",
+ },
+})
diff --git a/apps/mobile/app/components/TaskEpicPopup.tsx b/apps/mobile/app/components/TaskEpicPopup.tsx
new file mode 100644
index 000000000..2b3b1eb36
--- /dev/null
+++ b/apps/mobile/app/components/TaskEpicPopup.tsx
@@ -0,0 +1,187 @@
+/* eslint-disable react-native/no-inline-styles */
+/* eslint-disable react-native/no-color-literals */
+import React from "react"
+import { BlurView } from "expo-blur"
+import {
+ Animated,
+ Modal,
+ TouchableWithoutFeedback,
+ View,
+ Text,
+ ViewStyle,
+ StyleSheet,
+ FlatList,
+ TouchableOpacity,
+} from "react-native"
+import { Feather, AntDesign } from "@expo/vector-icons"
+import { useAppTheme } from "../theme"
+import { ITeamTask } from "../services/interfaces/ITask"
+import { formattedEpic } from "./TaskEpic"
+
+interface ITaskEpicPopup {
+ visible: boolean
+ onDismiss: () => unknown
+ onTaskSelect: (parentTask: ITeamTask | undefined) => Promise
+ epicsList: formattedEpic
+ currentEpic: string
+ teamTasks: ITeamTask[]
+}
+
+const TaskEpicPopup: React.FC = ({
+ visible,
+ onDismiss,
+ onTaskSelect,
+ epicsList,
+ currentEpic,
+ teamTasks,
+}) => {
+ const { colors } = useAppTheme()
+
+ const allEpics = Object.values(epicsList)
+
+ return (
+
+
+ (
+
+ )}
+ legacyImplementation={true}
+ showsVerticalScrollIndicator={true}
+ keyExtractor={(_, index) => index.toString()}
+ />
+
+
+ )
+}
+
+export default TaskEpicPopup
+
+interface ItemProps {
+ currentEpicId: string
+ onTaskSelect: (parentTask: ITeamTask | undefined) => Promise
+ epic: formattedEpic[keyof formattedEpic]
+ teamTasks: ITeamTask[]
+ onDismiss()
+}
+const Item: React.FC = ({ currentEpicId, epic, onTaskSelect, teamTasks, onDismiss }) => {
+ const { colors } = useAppTheme()
+ const selected = epic.id === currentEpicId
+
+ const epicTask = teamTasks.find((task) => task.id === epic.id)
+
+ return (
+ {
+ onTaskSelect(epicTask)
+ onDismiss()
+ }}
+ >
+
+
+ {epic.icon}
+ {epic.name}
+
+
+ {!selected ? (
+
+ ) : (
+
+ )}
+
+
+
+ )
+}
+
+const ModalPopUp = ({ visible, children, onDismiss }) => {
+ const [showModal, setShowModal] = React.useState(visible)
+ const scaleValue = React.useRef(new Animated.Value(0)).current
+
+ React.useEffect(() => {
+ toggleModal()
+ }, [visible])
+ const toggleModal = () => {
+ if (visible) {
+ setShowModal(true)
+ Animated.spring(scaleValue, {
+ toValue: 1,
+ useNativeDriver: true,
+ }).start()
+ } else {
+ setTimeout(() => setShowModal(false), 200)
+ Animated.timing(scaleValue, {
+ toValue: 0,
+ duration: 300,
+ useNativeDriver: true,
+ }).start()
+ }
+ }
+ return (
+
+
+ onDismiss()}>
+
+
+ {children}
+
+
+
+
+ )
+}
+
+const $modalBackGround: ViewStyle = {
+ flex: 1,
+ justifyContent: "center",
+}
+
+const styles = StyleSheet.create({
+ colorFrame: {
+ alignItems: "center",
+ borderRadius: 10,
+ flexDirection: "row",
+ gap: 5,
+ height: 44,
+ justifyContent: "center",
+ paddingLeft: 16,
+ width: 180,
+ },
+ container: {
+ alignSelf: "center",
+ backgroundColor: "#fff",
+ borderRadius: 20,
+ maxHeight: 396,
+ paddingHorizontal: 6,
+ paddingVertical: 16,
+ width: "80%",
+ },
+ wrapperItem: {
+ alignItems: "center",
+ borderColor: "rgba(0,0,0,0.13)",
+ borderRadius: 10,
+ borderWidth: 1,
+ flexDirection: "row",
+ justifyContent: "space-between",
+ marginBottom: 10,
+ padding: 2,
+ paddingRight: 18,
+ width: "100%",
+ },
+})
diff --git a/apps/mobile/app/components/TaskLabels.tsx b/apps/mobile/app/components/TaskLabels.tsx
index ac18409ed..b4362482b 100644
--- a/apps/mobile/app/components/TaskLabels.tsx
+++ b/apps/mobile/app/components/TaskLabels.tsx
@@ -1,274 +1,323 @@
/* eslint-disable react-native/no-color-literals */
/* eslint-disable react-native/no-inline-styles */
-import React, { FC, useEffect, useRef, useState } from 'react';
-import { TouchableOpacity, View, Text, StyleSheet, ViewStyle, FlatList } from 'react-native';
-import { AntDesign, Entypo } from '@expo/vector-icons';
-import { observer } from 'mobx-react-lite';
-import { ITeamTask } from '../services/interfaces/ITask';
-import { useTeamTasks } from '../services/hooks/features/useTeamTasks';
-import { useAppTheme, typography } from '../theme';
-import TaskLabelPopup from './TaskLabelPopup';
-import { ITaskLabelItem } from '../services/interfaces/ITaskLabel';
-import { translate } from '../i18n';
-import { limitTextCharaters } from '../helpers/sub-text';
-import { SvgUri } from 'react-native-svg';
-import { isEqual } from 'lodash';
+import React, { FC, useEffect, useRef, useState } from "react"
+import { TouchableOpacity, View, Text, StyleSheet, ViewStyle, FlatList } from "react-native"
+import { AntDesign, Entypo } from "@expo/vector-icons"
+import { observer } from "mobx-react-lite"
+import { ITeamTask } from "../services/interfaces/ITask"
+import { useTeamTasks } from "../services/hooks/features/useTeamTasks"
+import { useAppTheme, typography } from "../theme"
+import TaskLabelPopup from "./TaskLabelPopup"
+import { ITaskLabelItem } from "../services/interfaces/ITaskLabel"
+import { translate } from "../i18n"
+import { limitTextCharaters } from "../helpers/sub-text"
+import { SvgUri } from "react-native-svg"
+import { isEqual } from "lodash"
interface TaskLabelProps {
- task?: ITeamTask;
- containerStyle?: ViewStyle;
- labels?: string;
- setLabels?: (label: ITaskLabelItem[]) => unknown;
- newTaskLabels?: ITaskLabelItem[] | undefined;
+ task?: ITeamTask
+ containerStyle?: ViewStyle
+ labels?: string
+ setLabels?: (label: ITaskLabelItem[]) => unknown
+ newTaskLabels?: ITaskLabelItem[] | undefined
+ taskScreenButton?: boolean
+ noBorders?: boolean
}
interface IndividualTaskLabel {
- color: string;
- createdAt: string;
- description: string | null;
- fullIconUrl: string;
- icon: string;
- id: string;
- isSystem: boolean;
- name: string;
- organizationId: string;
- organizationTeamId: string;
- tenantId: string;
- updatedAt: string;
+ color: string
+ createdAt: string
+ description: string | null
+ fullIconUrl: string
+ icon: string
+ id: string
+ isSystem: boolean
+ name: string
+ organizationId: string
+ organizationTeamId: string
+ tenantId: string
+ updatedAt: string
}
-const TaskLabels: FC = observer(({ task, setLabels, newTaskLabels }) => {
- const { colors, dark } = useAppTheme();
- const { updateTask } = useTeamTasks();
- const [openModal, setOpenModal] = useState(false);
- const flatListRef = useRef(null);
- const [labelIndex, setLabelIndex] = useState(0);
- const [tempLabels, setTempLabels] = useState(task?.tags || newTaskLabels || []);
- const [arrayChanged, setArrayChanged] = useState(false);
+const TaskLabels: FC = observer(
+ ({ task, setLabels, newTaskLabels, taskScreenButton, noBorders, containerStyle }) => {
+ const { colors, dark } = useAppTheme()
+ const { updateTask } = useTeamTasks()
+ const [openModal, setOpenModal] = useState(false)
+ const flatListRef = useRef(null)
+ const [labelIndex, setLabelIndex] = useState(0)
+ const [tempLabels, setTempLabels] = useState(
+ task?.tags || newTaskLabels || [],
+ )
+ const [arrayChanged, setArrayChanged] = useState(false)
- const freshOpenModal = () => {
- setOpenModal(true);
- setTempLabels(task?.tags || newTaskLabels || []);
- arraysHaveSameValues(tempLabels, task?.tags || newTaskLabels || []);
- };
+ const freshOpenModal = () => {
+ setOpenModal(true)
+ setTempLabels(task?.tags || newTaskLabels || [])
+ arraysHaveSameValues(tempLabels, task?.tags || newTaskLabels || [])
+ }
- const saveLabels = async () => {
- if (task) {
- const taskEdit = {
- ...task,
- tags: tempLabels
- };
- await updateTask(taskEdit, task.id);
- } else {
- setLabels(tempLabels);
+ const saveLabels = async () => {
+ if (task) {
+ const taskEdit = {
+ ...task,
+ tags: tempLabels,
+ }
+ await updateTask(taskEdit, task.id)
+ } else {
+ setLabels(tempLabels)
+ }
+ setOpenModal(false)
}
- setOpenModal(false);
- };
- const addOrRemoveLabelsInTempArray = (tag: ITaskLabelItem): void => {
- const exist = tempLabels.find((label) => label.id === tag.id);
- if (exist) {
- setTempLabels(tempLabels.filter((label) => label.id !== tag.id));
- } else {
- setTempLabels([...tempLabels, tag]);
+ const addOrRemoveLabelsInTempArray = (tag: ITaskLabelItem): void => {
+ const exist = tempLabels.find((label) => label.id === tag.id)
+ if (exist) {
+ setTempLabels(tempLabels.filter((label) => label.id !== tag.id))
+ } else {
+ setTempLabels([...tempLabels, tag])
+ }
}
- };
- const arraysHaveSameValues = (array1: ITaskLabelItem[] | [], array2: ITaskLabelItem[] | []): void => {
- const sortedArray1 = array1.slice().sort((a, b) => a.id.localeCompare(b.id));
- const sortedArray2 = array2.slice().sort((a, b) => a.id.localeCompare(b.id));
+ const arraysHaveSameValues = (
+ array1: ITaskLabelItem[] | [],
+ array2: ITaskLabelItem[] | [],
+ ): void => {
+ const sortedArray1 = array1.slice().sort((a, b) => a.id.localeCompare(b.id))
+ const sortedArray2 = array2.slice().sort((a, b) => a.id.localeCompare(b.id))
- const areArraysEqual = isEqual(sortedArray1, sortedArray2);
+ const areArraysEqual = isEqual(sortedArray1, sortedArray2)
- setArrayChanged(!areArraysEqual);
- };
+ setArrayChanged(!areArraysEqual)
+ }
- useEffect(() => {
- arraysHaveSameValues(tempLabels, task?.tags || newTaskLabels || []);
- }, [tempLabels]);
+ useEffect(() => {
+ arraysHaveSameValues(tempLabels, task?.tags || newTaskLabels || [])
+ }, [tempLabels])
- const scrollToIndexWithDelay = (index: number) => {
- flatListRef.current?.scrollToIndex({
- animated: true,
- index: index < 0 ? 0 : index,
- viewPosition: 0
- });
- };
+ const scrollToIndexWithDelay = (index: number) => {
+ flatListRef.current?.scrollToIndex({
+ animated: true,
+ index: index < 0 ? 0 : index,
+ viewPosition: 0,
+ })
+ }
- const onNextPressed = () => {
- if (labelIndex !== task?.tags?.length - 2) {
- scrollToIndexWithDelay(labelIndex + 1);
+ const onNextPressed = () => {
+ if (labelIndex !== task?.tags?.length - 2) {
+ scrollToIndexWithDelay(labelIndex + 1)
+ }
}
- };
- const onPrevPressed = () => {
- if (labelIndex > 0) {
- const newIndex = labelIndex - 2;
- scrollToIndexWithDelay(newIndex);
+ const onPrevPressed = () => {
+ if (labelIndex > 0) {
+ const newIndex = labelIndex - 2
+ scrollToIndexWithDelay(newIndex)
+ }
}
- };
- const handleScrollEnd = (event: any) => {
- const offsetX = event.nativeEvent.contentOffset.x;
- const currentIndex = Math.round(offsetX / 100); // Assuming 100 is the item width
- setLabelIndex(currentIndex);
- };
+ const handleScrollEnd = (event: any) => {
+ const offsetX = event.nativeEvent.contentOffset.x
+ const currentIndex = Math.round(offsetX / 100) // Assuming 100 is the item width
+ setLabelIndex(currentIndex)
+ }
- return (
- <>
- setOpenModal(false)}
- canCreateLabel={true}
- />
- {(task?.tags !== undefined || newTaskLabels !== undefined) &&
- (task?.tags?.length > 0 || newTaskLabels?.length > 0) ? (
-
- }
- horizontal={true}
- keyExtractor={(_, index) => index.toString()}
- showsHorizontalScrollIndicator={false}
- ItemSeparatorComponent={() => (
-
+ return (
+ <>
+ setOpenModal(false)}
+ canCreateLabel={true}
+ />
+ {(task?.tags !== undefined || newTaskLabels !== undefined) &&
+ (task?.tags?.length > 0 || newTaskLabels?.length > 0) &&
+ !taskScreenButton ? (
+
+ (
+
+ )}
+ horizontal={true}
+ keyExtractor={(_, index) => index.toString()}
+ showsHorizontalScrollIndicator={false}
+ ItemSeparatorComponent={() => (
+
+ )}
+ onMomentumScrollEnd={handleScrollEnd}
+ />
+ {labelIndex >= (task?.tags?.length - 3 || newTaskLabels?.length - 3) ||
+ task?.tags?.length < 3 ||
+ newTaskLabels?.length < 3 ? null : (
+ onNextPressed()}
+ >
+
+
)}
- onMomentumScrollEnd={handleScrollEnd}
- />
- {labelIndex >= (task?.tags?.length - 3 || newTaskLabels?.length - 3) ||
- task?.tags?.length < 3 ||
- newTaskLabels?.length < 3 ? null : (
- onNextPressed()}
- >
-
-
- )}
- {labelIndex >= 1 ? (
- onPrevPressed()}
+ {labelIndex >= 1 ? (
+ onPrevPressed()}
+ >
+
+
+ ) : null}
+
+ ) : (task?.tags !== undefined || newTaskLabels !== undefined) &&
+ (task?.tags?.length > 0 || newTaskLabels?.length > 0) &&
+ taskScreenButton ? (
+
+ (
+
+ )}
+ // horizontal={true}
+ scrollEnabled={false}
+ keyExtractor={(_, index) => index.toString()}
+ showsHorizontalScrollIndicator={false}
+ // ItemSeparatorComponent={() => (
+ //
+ // )}
+ onMomentumScrollEnd={handleScrollEnd}
+ />
+
+ ) : (
+
+
-
-
- ) : null}
-
- ) : (
-
-
-
-
-
-
- {translate('settingScreen.labelScreen.labels')}
-
-
+
+
+
+
+ {translate("settingScreen.labelScreen.labels")}
+
+
-
-
-
- )}
- >
- );
-});
+
+
+
+ )}
+ >
+ )
+ },
+)
interface ILabel {
- item: IndividualTaskLabel | null;
- freshOpenModal: () => void;
+ item: IndividualTaskLabel | null
+ freshOpenModal: () => void
+ taskScreenButton?: boolean
+ noBorders?: boolean
}
-const Label: FC = ({ item, freshOpenModal }) => {
- const { colors } = useAppTheme();
+const Label: FC = ({ item, freshOpenModal, taskScreenButton, noBorders }) => {
+ const { colors } = useAppTheme()
return (
- {limitTextCharaters({ text: item?.name, numChars: 12 })}
+ {limitTextCharaters({ text: item?.name, numChars: taskScreenButton ? 15 : 12 })}
- );
-};
+ )
+}
const styles = StyleSheet.create({
container: {
- alignItems: 'center',
+ alignItems: "center",
borderRadius: 10,
borderWidth: 1,
- flexDirection: 'row',
+ flexDirection: "row",
height: 32,
- justifyContent: 'space-between',
+ justifyContent: "space-between",
marginVertical: 20,
paddingHorizontal: 12,
paddingVertical: 7,
- width: 160
+ width: 160,
},
scrollButtons: {
- alignItems: 'center',
- backgroundColor: '#fff',
+ alignItems: "center",
+ backgroundColor: "#fff",
borderRadius: 20,
bottom: 23,
elevation: 10,
height: 27,
- justifyContent: 'center',
+ justifyContent: "center",
padding: 5,
- position: 'absolute',
- shadowColor: 'rgba(0,0,0,0.16)',
+ position: "absolute",
+ shadowColor: "rgba(0,0,0,0.16)",
shadowOffset: { width: 0, height: 5 },
shadowOpacity: 1,
shadowRadius: 15,
- width: 28
+ width: 28,
},
text: {
fontFamily: typography.fonts.PlusJakartaSans.semiBold,
- fontSize: 10
+ fontSize: 10,
},
wrapStatus: {
- alignItems: 'center',
- flexDirection: 'row',
- width: '70%'
- }
-});
+ alignItems: "center",
+ flexDirection: "row",
+ width: "70%",
+ },
+})
-export default TaskLabels;
+export default TaskLabels
diff --git a/apps/mobile/app/components/TaskStatus.tsx b/apps/mobile/app/components/TaskStatus.tsx
index 2f248f960..e28f4f9c9 100644
--- a/apps/mobile/app/components/TaskStatus.tsx
+++ b/apps/mobile/app/components/TaskStatus.tsx
@@ -1,115 +1,131 @@
/* eslint-disable react-native/no-color-literals */
/* eslint-disable react-native/no-inline-styles */
-import React, { FC, useState } from 'react';
-import { TouchableOpacity, View, Text, StyleSheet, ViewStyle, TextStyle } from 'react-native';
-import { AntDesign, Feather } from '@expo/vector-icons';
-import { ITaskStatus, ITeamTask } from '../services/interfaces/ITask';
-import { observer } from 'mobx-react-lite';
-import { useTeamTasks } from '../services/hooks/features/useTeamTasks';
-import TaskStatusPopup from './TaskStatusPopup';
-import { typography, useAppTheme } from '../theme';
-import { translate } from '../i18n';
-import { useTaskStatusValue } from './StatusType';
-import { limitTextCharaters } from '../helpers/sub-text';
+import React, { FC, useState } from "react"
+import { TouchableOpacity, View, Text, StyleSheet, ViewStyle, TextStyle } from "react-native"
+import { AntDesign, Feather } from "@expo/vector-icons"
+import { ITaskStatus, ITeamTask } from "../services/interfaces/ITask"
+import { observer } from "mobx-react-lite"
+import { useTeamTasks } from "../services/hooks/features/useTeamTasks"
+import TaskStatusPopup from "./TaskStatusPopup"
+import { typography, useAppTheme } from "../theme"
+import { translate } from "../i18n"
+import { useTaskStatusValue } from "./StatusType"
+import { limitTextCharaters } from "../helpers/sub-text"
interface TaskStatusProps {
- task?: ITeamTask;
- containerStyle?: ViewStyle;
- statusTextSyle?: TextStyle;
- iconsOnly?: boolean;
- status?: string;
- setStatus?: (status: string) => unknown;
+ task?: ITeamTask
+ containerStyle?: ViewStyle
+ statusTextSyle?: TextStyle
+ iconsOnly?: boolean
+ status?: string
+ setStatus?: (status: string) => unknown
}
-const TaskStatus: FC = observer(({ task, containerStyle, status, setStatus, iconsOnly }) => {
- const { colors, dark } = useAppTheme();
- const { updateTask } = useTeamTasks();
- const [openModal, setOpenModal] = useState(false);
+const TaskStatus: FC = observer(
+ ({ task, containerStyle, status, setStatus, iconsOnly }) => {
+ const { colors, dark } = useAppTheme()
+ const { updateTask } = useTeamTasks()
+ const [openModal, setOpenModal] = useState(false)
- const allStatuses = useTaskStatusValue();
+ const allStatuses = useTaskStatusValue()
- const statusValue = (task?.status?.split('-').join(' ') || (status && status.split('-').join(' ')))?.toLowerCase();
- const statusItem =
- allStatuses && Object.values(allStatuses).find((item) => item?.name.toLowerCase() === statusValue);
+ const statusValue = (
+ task?.status?.split("-").join(" ") ||
+ (status && status.split("-").join(" "))
+ )?.toLowerCase()
+ const statusItem =
+ allStatuses &&
+ Object.values(allStatuses).find((item) => item?.name.toLowerCase() === statusValue)
- const onChangeStatus = async (text) => {
- if (task) {
- const value: ITaskStatus = text;
- const taskEdit = {
- ...task,
- status: value
- };
+ const onChangeStatus = async (text) => {
+ if (task) {
+ const value: ITaskStatus = text
+ const taskEdit = {
+ ...task,
+ status: value,
+ }
- await updateTask(taskEdit, task.id);
- } else {
- setStatus(text);
+ await updateTask(taskEdit, task.id)
+ } else {
+ setStatus(text)
+ }
}
- };
- return (
- <>
- onChangeStatus(e)}
- onDismiss={() => setOpenModal(false)}
- />
- setOpenModal(true)}>
-
- {statusItem ? (
-
- {statusItem.icon}
- {iconsOnly ? null : (
-
- {limitTextCharaters({ text: statusItem?.name, numChars: 11 })}
-
- )}
-
- ) : (
-
- {iconsOnly ? (
-
- ) : (
- translate('settingScreen.statusScreen.statuses')
- )}
-
- )}
-
-
-
- >
- );
-});
+ return (
+ <>
+ onChangeStatus(e)}
+ onDismiss={() => setOpenModal(false)}
+ />
+ setOpenModal(true)}>
+
+ {statusItem ? (
+
+ {statusItem.icon}
+ {iconsOnly ? null : (
+
+ {limitTextCharaters({
+ text: statusItem?.name,
+ numChars: 11,
+ })}
+
+ )}
+
+ ) : (
+
+ {iconsOnly ? (
+
+ ) : (
+ translate("settingScreen.statusScreen.statuses")
+ )}
+
+ )}
+
+
+
+ >
+ )
+ },
+)
const styles = StyleSheet.create({
container: {
- alignItems: 'center',
+ alignItems: "center",
borderRadius: 10,
- flexDirection: 'row',
- justifyContent: 'space-between',
+ flexDirection: "row",
+ justifyContent: "space-between",
minHeight: 30,
- paddingHorizontal: 8
+ paddingHorizontal: 8,
},
text: {
fontFamily: typography.fonts.PlusJakartaSans.semiBold,
fontSize: 10,
- textTransform: 'capitalize'
+ textTransform: "capitalize",
},
wrapStatus: {
- alignItems: 'center',
- flexDirection: 'row'
- }
-});
+ alignItems: "center",
+ flexDirection: "row",
+ },
+})
-export default TaskStatus;
+export default TaskStatus
diff --git a/apps/mobile/app/components/TaskVersion.tsx b/apps/mobile/app/components/TaskVersion.tsx
new file mode 100644
index 000000000..5ffbe7398
--- /dev/null
+++ b/apps/mobile/app/components/TaskVersion.tsx
@@ -0,0 +1,124 @@
+/* eslint-disable react-native/no-color-literals */
+/* eslint-disable react-native/no-inline-styles */
+import React, { FC, useState } from "react"
+import { TouchableOpacity, View, Text, StyleSheet, ViewStyle } from "react-native"
+import { AntDesign, Entypo } from "@expo/vector-icons"
+import { observer } from "mobx-react-lite"
+import { ITeamTask } from "../services/interfaces/ITask"
+import { useTeamTasks } from "../services/hooks/features/useTeamTasks"
+import { typography, useAppTheme } from "../theme"
+import { translate } from "../i18n"
+import { useTaskVersionValue } from "./StatusType"
+import { limitTextCharaters } from "../helpers/sub-text"
+import TaskVersionPopup from "./TaskVersionPopup"
+
+interface TaskVersionProps {
+ task?: ITeamTask
+ containerStyle?: ViewStyle
+ version?: string
+ setPriority?: (priority: string) => unknown
+}
+
+const TaskVersion: FC = observer(
+ ({ task, containerStyle, version, setPriority }) => {
+ const { colors } = useAppTheme()
+ const { updateTask } = useTeamTasks()
+ const [openModal, setOpenModal] = useState(false)
+
+ const allTaskVersions = useTaskVersionValue()
+
+ const versionValue = (task?.version || (version && version))?.toLowerCase()
+
+ const currentVersion =
+ allTaskVersions &&
+ Object.values(allTaskVersions).find((item) => item.value.toLowerCase() === versionValue)
+
+ const onChangeVersion = async (text) => {
+ if (task) {
+ const taskEdit = {
+ ...task,
+ version: text,
+ }
+
+ await updateTask(taskEdit, task.id)
+ } else {
+ setPriority(text)
+ }
+ }
+
+ return (
+ <>
+ onChangeVersion(e.value)}
+ onDismiss={() => setOpenModal(false)}
+ />
+ setOpenModal(true)}>
+
+ {(task?.version || version) && currentVersion ? (
+
+ {currentVersion.icon}
+
+ {limitTextCharaters({
+ text: currentVersion.name,
+ numChars: 15,
+ })}
+
+
+ ) : (
+
+
+
+ {translate("taskDetailsScreen.version")}
+
+
+ )}
+
+
+
+ >
+ )
+ },
+)
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: "center",
+ borderColor: "rgba(0,0,0,0.16)",
+ borderRadius: 10,
+ borderWidth: 1,
+ flexDirection: "row",
+ justifyContent: "space-between",
+ minHeight: 30,
+ minWidth: 100,
+ paddingHorizontal: 8,
+ },
+ text: {
+ fontFamily: typography.fonts.PlusJakartaSans.semiBold,
+ fontSize: 10,
+ textTransform: "capitalize",
+ },
+ wrapStatus: {
+ alignItems: "center",
+ flexDirection: "row",
+ width: "70%",
+ },
+})
+
+export default TaskVersion
diff --git a/apps/mobile/app/components/TaskVersionPopup.tsx b/apps/mobile/app/components/TaskVersionPopup.tsx
new file mode 100644
index 000000000..0c5c1349c
--- /dev/null
+++ b/apps/mobile/app/components/TaskVersionPopup.tsx
@@ -0,0 +1,180 @@
+/* eslint-disable react-native/no-inline-styles */
+/* eslint-disable react-native/no-color-literals */
+import React, { FC } from "react"
+import {
+ View,
+ ViewStyle,
+ Modal,
+ Animated,
+ StyleSheet,
+ Text,
+ FlatList,
+ TouchableOpacity,
+ TouchableWithoutFeedback,
+} from "react-native"
+import { Feather, AntDesign } from "@expo/vector-icons"
+import { spacing, useAppTheme } from "../theme"
+import { ITaskPriorityItem } from "../services/interfaces/ITaskPriority"
+// import { translate } from "../i18n"
+import { BlurView } from "expo-blur"
+import { useTaskVersion } from "../services/hooks/features/useTaskVersion"
+import { BadgedTaskVersion } from "./VersionIcon"
+import { ITaskVersionItemList } from "../services/interfaces/ITaskVersion"
+
+export interface Props {
+ visible: boolean
+ onDismiss: () => unknown
+ versionName: string
+ setSelectedVersion: (status: ITaskVersionItemList) => unknown
+}
+
+const TaskVersionPopup: FC = function TaskPriorityPopup({
+ visible,
+ onDismiss,
+ setSelectedVersion,
+ versionName,
+}) {
+ const { taskVersionList } = useTaskVersion()
+ const { colors } = useAppTheme()
+ const onVersionSelected = (size: ITaskPriorityItem) => {
+ setSelectedVersion(size)
+ onDismiss()
+ }
+
+ return (
+
+
+ Versions
+ (
+
+ )}
+ legacyImplementation={true}
+ showsVerticalScrollIndicator={true}
+ keyExtractor={(_, index) => index.toString()}
+ />
+
+
+ )
+}
+
+export default TaskVersionPopup
+
+interface ItemProps {
+ currentVersionName: string
+ version: ITaskPriorityItem
+ onVersionSelected: (size: ITaskPriorityItem) => unknown
+}
+const Item: FC = ({ currentVersionName, version, onVersionSelected }) => {
+ const { colors } = useAppTheme()
+ const selected = version.value === currentVersionName
+
+ return (
+ onVersionSelected(version)}>
+
+
+
+
+
+ {!selected ? (
+
+ ) : (
+
+ )}
+
+
+
+ )
+}
+
+const ModalPopUp = ({ visible, children, onDismiss }) => {
+ const [showModal, setShowModal] = React.useState(visible)
+ const scaleValue = React.useRef(new Animated.Value(0)).current
+
+ React.useEffect(() => {
+ toggleModal()
+ }, [visible])
+ const toggleModal = () => {
+ if (visible) {
+ setShowModal(true)
+ Animated.spring(scaleValue, {
+ toValue: 1,
+ useNativeDriver: true,
+ }).start()
+ } else {
+ setTimeout(() => setShowModal(false), 200)
+ Animated.timing(scaleValue, {
+ toValue: 0,
+ duration: 300,
+ useNativeDriver: true,
+ }).start()
+ }
+ }
+ return (
+
+
+ onDismiss()}>
+
+
+ {children}
+
+
+
+
+ )
+}
+
+const $modalBackGround: ViewStyle = {
+ flex: 1,
+ justifyContent: "center",
+}
+
+const styles = StyleSheet.create({
+ colorFrame: {
+ borderRadius: 10,
+ height: 44,
+ justifyContent: "center",
+ paddingLeft: 16,
+ width: 180,
+ },
+ container: {
+ alignSelf: "center",
+ backgroundColor: "#fff",
+ borderRadius: 20,
+ maxHeight: 396,
+ paddingHorizontal: 6,
+ paddingVertical: 16,
+ width: "90%",
+ },
+ title: {
+ fontSize: spacing.medium - 2,
+ marginBottom: 16,
+ marginHorizontal: 10,
+ },
+ wrapperItem: {
+ alignItems: "center",
+ borderColor: "rgba(0,0,0,0.13)",
+ borderRadius: 10,
+ borderWidth: 1,
+ flexDirection: "row",
+ justifyContent: "space-between",
+ marginBottom: 10,
+ padding: 6,
+ paddingRight: 18,
+ width: "100%",
+ },
+})
diff --git a/apps/mobile/app/components/VersionIcon.tsx b/apps/mobile/app/components/VersionIcon.tsx
new file mode 100644
index 000000000..61219a604
--- /dev/null
+++ b/apps/mobile/app/components/VersionIcon.tsx
@@ -0,0 +1,42 @@
+/* eslint-disable react-native/no-color-literals */
+/* eslint-disable react-native/no-inline-styles */
+import React, { useMemo } from "react"
+import { View, Text } from "react-native"
+import { SvgUri } from "react-native-svg"
+import { typography } from "../theme"
+import { observer } from "mobx-react-lite"
+import { limitTextCharaters } from "../helpers/sub-text"
+import { useTaskVersion } from "../services/hooks/features/useTaskVersion"
+
+export const BadgedTaskVersion = observer(
+ ({ version, TextSize, iconSize }: { version: string; TextSize: number; iconSize: number }) => {
+ const { taskVersionList } = useTaskVersion()
+
+ const currentSize = useMemo(
+ () => taskVersionList.find((s) => s.name === version),
+ [version, taskVersionList],
+ )
+
+ return (
+
+
+
+ {limitTextCharaters({ text: currentSize?.name, numChars: 15 })}
+
+
+ )
+ },
+)
diff --git a/apps/mobile/app/components/svgs/icons.tsx b/apps/mobile/app/components/svgs/icons.tsx
index 48dcd2d17..b84a03ceb 100644
--- a/apps/mobile/app/components/svgs/icons.tsx
+++ b/apps/mobile/app/components/svgs/icons.tsx
@@ -591,3 +591,233 @@ export const copyIcon = `
`
+// Task Details
+
+export const globeLightTheme = ` `
+
+export const globeDarkTheme = ` `
+
+export const lockLightTheme = ``
+
+export const lockDarkTheme = ``
+
+export const clipboardIcon = ``
+
+export const profileIcon = `
+`
+export const peopleIconSmall = `
+`
+export const settingsIconSmall = `
+`
+
+export const trashIconLarge = ``
+
+export const trashIconSmall = ``
+
+export const calendarIcon = `
+`
+export const categoryIcon = `
+`
diff --git a/apps/mobile/app/i18n/ar.ts b/apps/mobile/app/i18n/ar.ts
index 58422e238..6fc65e63b 100644
--- a/apps/mobile/app/i18n/ar.ts
+++ b/apps/mobile/app/i18n/ar.ts
@@ -97,6 +97,27 @@ const ar: Translations = {
copyTitle: "تم نسخ العنوان.",
changeParent: "تغيير الوالدين",
addParent: "أضف أحد الوالدين",
+ taskScreen: "شاشة المهام",
+ details: "تفاصيل",
+ taskPublic: "هذه المهمة عامة",
+ makePrivate: "جعل خاص",
+ taskPrivate: "هذه المهمة خاصة",
+ makePublic: "جعل العامة",
+ typeIssue: "نوع المشكلة",
+ creator: "المُنشئ",
+ assignees: "المُنفذون",
+ startDate: "تاريخ البدء",
+ dueDate: "تاريخ الاستحقاق",
+ daysRemaining: "الأيام المتبقية",
+ version: "الإصدار",
+ epic: "ملحمة",
+ status: "الحالة",
+ labels: "التسميات",
+ size: "الحجم",
+ priority: "الأولوية",
+ manageAssignees: "إدارة المكلفين",
+ setDueDate: "تحديد تاريخ الاستحقاق",
+ setStartDate: "تحديد تاريخ البدء",
},
tasksScreen: {
name: "مهام",
diff --git a/apps/mobile/app/i18n/bg.ts b/apps/mobile/app/i18n/bg.ts
index 1b6fc8259..21de8b139 100644
--- a/apps/mobile/app/i18n/bg.ts
+++ b/apps/mobile/app/i18n/bg.ts
@@ -93,6 +93,27 @@ const bg = {
copyTitle: "Title Copied.",
changeParent: "Change Parent",
addParent: "Add Parent",
+ taskScreen: "Task Screen",
+ details: "Details",
+ taskPublic: "This task is Public",
+ makePrivate: "Make a Private",
+ taskPrivate: "This task is Private",
+ makePublic: "Make a Public",
+ typeIssue: "Type of Issue",
+ creator: "Creator",
+ assignees: "Assignees",
+ startDate: "Start Date",
+ dueDate: "Due Date",
+ daysRemaining: "Days Remaining",
+ version: "Version",
+ epic: "Epic",
+ status: "Status",
+ labels: "Labels",
+ size: "Size",
+ priority: "Priority",
+ manageAssignees: "Manage Assignees",
+ setDueDate: "Set Due Date",
+ setStartDate: "Set Start Date",
},
tasksScreen: {
name: "Tasks",
diff --git a/apps/mobile/app/i18n/en.ts b/apps/mobile/app/i18n/en.ts
index 2ad2082fa..5fd9145c8 100644
--- a/apps/mobile/app/i18n/en.ts
+++ b/apps/mobile/app/i18n/en.ts
@@ -94,6 +94,27 @@ const en = {
copyTitle: "Title Copied.",
changeParent: "Change Parent",
addParent: "Add Parent",
+ taskScreen: "Task Screen",
+ details: "Details",
+ taskPublic: "This task is Public",
+ makePrivate: "Make a Private",
+ taskPrivate: "This task is Private",
+ makePublic: "Make a Public",
+ typeIssue: "Type of Issue",
+ creator: "Creator",
+ assignees: "Assignees",
+ startDate: "Start Date",
+ dueDate: "Due Date",
+ daysRemaining: "Days Remaining",
+ version: "Version",
+ epic: "Epic",
+ status: "Status",
+ labels: "Labels",
+ size: "Size",
+ priority: "Priority",
+ manageAssignees: "Manage Assignees",
+ setDueDate: "Set Due Date",
+ setStartDate: "Set Start Date",
},
tasksScreen: {
name: "Tasks",
diff --git a/apps/mobile/app/i18n/es.ts b/apps/mobile/app/i18n/es.ts
index cfb450cd0..c2f29404b 100644
--- a/apps/mobile/app/i18n/es.ts
+++ b/apps/mobile/app/i18n/es.ts
@@ -93,6 +93,27 @@ const es = {
copyTitle: "Title Copied.",
changeParent: "Change Parent",
addParent: "Add Parent",
+ taskScreen: "Task Screen",
+ details: "Details",
+ taskPublic: "This task is Public",
+ makePrivate: "Make a Private",
+ taskPrivate: "This task is Private",
+ makePublic: "Make a Public",
+ typeIssue: "Type of Issue",
+ creator: "Creator",
+ assignees: "Assignees",
+ startDate: "Start Date",
+ dueDate: "Due Date",
+ daysRemaining: "Days Remaining",
+ version: "Version",
+ epic: "Epic",
+ status: "Status",
+ labels: "Labels",
+ size: "Size",
+ priority: "Priority",
+ manageAssignees: "Manage Assignees",
+ setDueDate: "Set Due Date",
+ setStartDate: "Set Start Date",
},
tasksScreen: {
name: "Tasks",
diff --git a/apps/mobile/app/i18n/fr.ts b/apps/mobile/app/i18n/fr.ts
index 53f5d6c86..6000d24ae 100644
--- a/apps/mobile/app/i18n/fr.ts
+++ b/apps/mobile/app/i18n/fr.ts
@@ -96,6 +96,27 @@ const fr = {
copyTitle: "Titre copié.",
changeParent: "Changer de parent",
addParent: "Ajouter un parent",
+ taskScreen: "Écran des tâches",
+ details: "Détails",
+ taskPublic: "Cette tâche est publique",
+ makePrivate: "Créer un privé",
+ taskPrivate: "Cette tâche est privée",
+ makePublic: "Faire un public",
+ typeIssue: "Type d'Issue",
+ creator: "Créateur",
+ assignees: "Bénéficiaires",
+ startDate: "Date de début",
+ dueDate: "Date d'échéance",
+ daysRemaining: "Jours restants",
+ version: "Version",
+ epic: "Épique",
+ status: "Statut",
+ labels: "Étiquettes",
+ size: "Taille",
+ priority: "Priorité",
+ manageAssignees: "Gérer les destinataires",
+ setDueDate: "Définir la date d'échéance",
+ setStartDate: "Définir la date de début",
},
tasksScreen: {
name: "Tâches",
diff --git a/apps/mobile/app/i18n/he.ts b/apps/mobile/app/i18n/he.ts
index d8a5cd820..ab5b47f40 100644
--- a/apps/mobile/app/i18n/he.ts
+++ b/apps/mobile/app/i18n/he.ts
@@ -93,6 +93,27 @@ const he = {
copyTitle: "Title Copied.",
changeParent: "Change Parent",
addParent: "Add Parent",
+ taskScreen: "Task Screen",
+ details: "Details",
+ taskPublic: "This task is Public",
+ makePrivate: "Make a Private",
+ taskPrivate: "This task is Private",
+ makePublic: "Make a Public",
+ typeIssue: "Type of Issue",
+ creator: "Creator",
+ assignees: "Assignees",
+ startDate: "Start Date",
+ dueDate: "Due Date",
+ daysRemaining: "Days Remaining",
+ version: "Version",
+ epic: "Epic",
+ status: "Status",
+ labels: "Labels",
+ size: "Size",
+ priority: "Priority",
+ manageAssignees: "Manage Assignees",
+ setDueDate: "Set Due Date",
+ setStartDate: "Set Start Date",
},
tasksScreen: {
name: "Tasks",
diff --git a/apps/mobile/app/i18n/ko.ts b/apps/mobile/app/i18n/ko.ts
index 1129759a8..7f34d361d 100644
--- a/apps/mobile/app/i18n/ko.ts
+++ b/apps/mobile/app/i18n/ko.ts
@@ -96,6 +96,27 @@ const ko: Translations = {
copyTitle: "제목이 복사되었습니다.",
changeParent: "상위 변경",
addParent: "상위 추가",
+ taskScreen: "작업 화면",
+ details: "세부",
+ taskPublic: "이 작업은 공개입니다.",
+ makePrivate: "비공개로 설정",
+ taskPrivate: "이 작업은 비공개입니다.",
+ makePublic: "공개로 설정",
+ typeIssue: "이슈 유형",
+ creator: "생성자",
+ assignees: "담당자",
+ startDate: "시작일",
+ dueDate: "마감일",
+ daysRemaining: "남은 일수",
+ version: "버전",
+ epic: "에픽",
+ status: "상태",
+ labels: "라벨",
+ size: "크기",
+ priority: "우선 순위",
+ manageAssignees: "담당자 관리",
+ setDueDate: "마감일 설정",
+ setStartDate: "시작일 설정",
},
tasksScreen: {
name: "작업",
diff --git a/apps/mobile/app/i18n/ru.ts b/apps/mobile/app/i18n/ru.ts
index 1d0117d5d..6833e2e73 100644
--- a/apps/mobile/app/i18n/ru.ts
+++ b/apps/mobile/app/i18n/ru.ts
@@ -93,6 +93,27 @@ const ru = {
copyTitle: "Title Copied.",
changeParent: "Change Parent",
addParent: "Add Parent",
+ taskScreen: "Task Screen",
+ details: "Details",
+ taskPublic: "This task is Public",
+ makePrivate: "Make a Private",
+ taskPrivate: "This task is Private",
+ makePublic: "Make a Public",
+ typeIssue: "Type of Issue",
+ creator: "Creator",
+ assignees: "Assignees",
+ startDate: "Start Date",
+ dueDate: "Due Date",
+ daysRemaining: "Days Remaining",
+ version: "Version",
+ epic: "Epic",
+ status: "Status",
+ labels: "Labels",
+ size: "Size",
+ priority: "Priority",
+ manageAssignees: "Manage Assignees",
+ setDueDate: "Set Due Date",
+ setStartDate: "Set Start Date",
},
tasksScreen: {
name: "Tasks",
diff --git a/apps/mobile/app/navigators/AuthenticatedNavigator.tsx b/apps/mobile/app/navigators/AuthenticatedNavigator.tsx
index 30f420187..6143ced2d 100644
--- a/apps/mobile/app/navigators/AuthenticatedNavigator.tsx
+++ b/apps/mobile/app/navigators/AuthenticatedNavigator.tsx
@@ -86,6 +86,12 @@ export type SettingScreenNavigationProp
>
+export type DrawerNavigationProp =
+ CompositeNavigationProp<
+ BottomTabNavigationProp,
+ StackNavigationProp
+ >
+
export type SettingScreenRouteProp = RouteProp<
AuthenticatedTabParamList,
T
diff --git a/apps/mobile/app/screens/Authenticated/TaskScreen/index.tsx b/apps/mobile/app/screens/Authenticated/TaskScreen/index.tsx
index dfe3deaff..efb7b6331 100644
--- a/apps/mobile/app/screens/Authenticated/TaskScreen/index.tsx
+++ b/apps/mobile/app/screens/Authenticated/TaskScreen/index.tsx
@@ -1,14 +1,14 @@
/* eslint-disable react-native/no-inline-styles */
-import { View, Text, ViewStyle, TouchableOpacity, StyleSheet } from "react-native"
+import { View, Text, ViewStyle, TouchableOpacity, StyleSheet, ScrollView } from "react-native"
import React, { FC, useEffect } from "react"
import { AuthenticatedDrawerScreenProps } from "../../../navigators/AuthenticatedNavigator"
import { Screen } from "../../../components"
-import Animated from "react-native-reanimated"
import { typography, useAppTheme } from "../../../theme"
import { AntDesign } from "@expo/vector-icons"
import { useTeamTasks } from "../../../services/hooks/features/useTeamTasks"
import TaskTitleBlock from "../../../components/Task/TitleBlock"
-// import { translate } from "../../../i18n"
+import DetailsBlock from "../../../components/Task/DetailsBlock"
+import { translate } from "../../../i18n"
export const AuthenticatedTaskScreen: FC> = (
_props,
@@ -16,7 +16,6 @@ export const AuthenticatedTaskScreen: FC {
@@ -27,24 +26,35 @@ export const AuthenticatedTaskScreen: FC
-
+
navigation.navigate("AuthenticatedTab")}>
- Task Screen
+
+ {translate("taskDetailsScreen.taskScreen")}
+
-
-
+
+
+
+
+
-
+
)
}
@@ -73,6 +83,13 @@ const styles = StyleSheet.create({
flexDirection: "row",
width: "100%",
},
+ screenContentWrapper: {
+ alignItems: "center",
+ flex: 4,
+ gap: 12,
+ paddingBottom: 20,
+ width: "100%",
+ },
title: {
alignSelf: "center",
diff --git a/apps/mobile/app/services/client/queries/task/task-version.ts b/apps/mobile/app/services/client/queries/task/task-version.ts
new file mode 100644
index 000000000..0dcc31537
--- /dev/null
+++ b/apps/mobile/app/services/client/queries/task/task-version.ts
@@ -0,0 +1,27 @@
+import { useQuery } from "react-query"
+import { getTaskVersionListRequest } from "../../requests/task-version"
+
+interface IGetTaskVersionsParams {
+ authToken: string
+ tenantId: string
+ organizationId: string
+ activeTeamId: string
+}
+const fetchAllVersions = async (params: IGetTaskVersionsParams) => {
+ const { organizationId, tenantId, activeTeamId, authToken } = params
+ const { data } = await getTaskVersionListRequest(
+ {
+ tenantId,
+ organizationId,
+ activeTeamId,
+ },
+ authToken,
+ )
+ return data
+}
+
+const useFetchAllVersions = (IGetTaskVersionsParams) =>
+ useQuery(["versions", IGetTaskVersionsParams], () => fetchAllVersions(IGetTaskVersionsParams), {
+ refetchInterval: 62000,
+ })
+export default useFetchAllVersions
diff --git a/apps/mobile/app/services/client/requests/task-version.ts b/apps/mobile/app/services/client/requests/task-version.ts
new file mode 100644
index 000000000..4c300c850
--- /dev/null
+++ b/apps/mobile/app/services/client/requests/task-version.ts
@@ -0,0 +1,73 @@
+/* eslint-disable camelcase */
+import { ITaskVersionCreate, ITaskVersionItemList } from "../../interfaces/ITaskVersion"
+import { serverFetch } from "../fetch"
+
+export function createVersionRequest(
+ datas: ITaskVersionCreate,
+ bearer_token: string,
+ tenantId?: any,
+) {
+ return serverFetch({
+ path: "/task-versions",
+ method: "POST",
+ body: datas,
+ bearer_token,
+ tenantId,
+ })
+}
+
+export function editTaskVersionRequest({
+ id,
+ datas,
+ bearer_token,
+ tenantId,
+}: {
+ id: string | any
+ datas: ITaskVersionCreate
+ bearer_token: string
+ tenantId?: any
+}) {
+ return serverFetch({
+ path: `/task-versions/${id}`,
+ method: "PUT",
+ body: datas,
+ bearer_token,
+ tenantId,
+ })
+}
+
+export function deleteTaskVersionRequest({
+ id,
+ bearer_token,
+ tenantId,
+}: {
+ id: string | any
+ bearer_token: string | any
+ tenantId?: any
+}) {
+ return serverFetch({
+ path: `/task-versions/${id}`,
+ method: "DELETE",
+ bearer_token,
+ tenantId,
+ })
+}
+
+export function getTaskVersionListRequest(
+ {
+ organizationId,
+ tenantId,
+ activeTeamId,
+ }: {
+ tenantId: string
+ organizationId: string
+ activeTeamId: string | null
+ },
+ bearer_token: string,
+) {
+ return serverFetch({
+ path: `/task-versions?tenantId=${tenantId}&organizationId=${organizationId}&organizationTeamId=${activeTeamId}`,
+ method: "GET",
+ bearer_token,
+ })
+}
diff --git a/apps/mobile/app/services/hooks/features/useTaskVersion.ts b/apps/mobile/app/services/hooks/features/useTaskVersion.ts
new file mode 100644
index 000000000..95e82a49d
--- /dev/null
+++ b/apps/mobile/app/services/hooks/features/useTaskVersion.ts
@@ -0,0 +1,70 @@
+import { useQueryClient } from "react-query"
+import {
+ createVersionRequest,
+ deleteTaskVersionRequest,
+ editTaskVersionRequest,
+} from "../../client/requests/task-version"
+import { ITaskVersionCreate, ITaskVersionItemList } from "../../interfaces/ITaskVersion"
+import { useStores } from "../../../models"
+import { useCallback, useEffect, useState } from "react"
+import useFetchAllVersions from "../../client/queries/task/task-version"
+
+export function useTaskVersion() {
+ const queryClient = useQueryClient()
+ const {
+ authenticationStore: { authToken, tenantId, organizationId },
+ teamStore: { activeTeamId },
+ } = useStores()
+
+ const [taskVersionList, setTaskVersionList] = useState([])
+
+ const {
+ data: versions,
+ isLoading,
+ isSuccess,
+ isRefetching,
+ } = useFetchAllVersions({ tenantId, organizationId, activeTeamId, authToken })
+
+ const createTaskVersion = useCallback(
+ async (data: ITaskVersionCreate) => {
+ await createVersionRequest(data, authToken, tenantId)
+ queryClient.invalidateQueries("versions")
+ },
+ [authToken, tenantId, queryClient],
+ )
+
+ const deleteTaskVersion = useCallback(
+ async (id: string) => {
+ await deleteTaskVersionRequest({ bearer_token: authToken, tenantId, id })
+
+ queryClient.invalidateQueries("versions")
+ },
+ [authToken, tenantId, queryClient],
+ )
+
+ const updateTaskVersion = useCallback(
+ async ({ id, data }) => {
+ await editTaskVersionRequest({ id, datas: data, bearer_token: authToken, tenantId })
+ queryClient.invalidateQueries("versions")
+ },
+ [authToken, tenantId, queryClient],
+ )
+
+ useEffect(() => {
+ if (isSuccess) {
+ if (versions) {
+ // @ts-ignore
+ setTaskVersionList(versions?.items || [])
+ }
+ }
+ }, [isLoading, isRefetching])
+
+ return {
+ createTaskVersion,
+ deleteTaskVersion,
+ updateTaskVersion,
+ taskVersionList,
+ versions,
+ isLoading,
+ }
+}
diff --git a/apps/mobile/app/services/hooks/features/useTeamTasks.ts b/apps/mobile/app/services/hooks/features/useTeamTasks.ts
index 47c90e3b5..75bceca4d 100644
--- a/apps/mobile/app/services/hooks/features/useTeamTasks.ts
+++ b/apps/mobile/app/services/hooks/features/useTeamTasks.ts
@@ -243,6 +243,26 @@ export function useTeamTasks() {
[],
)
+ const updatePublicity = useCallback(
+ (publicity: boolean, task?: ITeamTask | null, loader?: boolean) => {
+ if (task && publicity !== task.public) {
+ loader && setTasksFetching(true)
+ return updateTask(
+ {
+ ...task,
+ public: publicity,
+ },
+ task.id,
+ ).then((res) => {
+ setTasksFetching(false)
+ return res
+ })
+ }
+ return Promise.resolve()
+ },
+ [setTasksFetching],
+ )
+
/**
* Change active task
*/
@@ -308,6 +328,7 @@ export function useTeamTasks() {
detailedTask,
deleteTask,
updateTask,
+ updatePublicity,
setActiveTeamTask,
updateDescription,
updateTitle,
diff --git a/apps/mobile/app/services/interfaces/ITask.ts b/apps/mobile/app/services/interfaces/ITask.ts
index 200c4f7fb..b6c13fc88 100644
--- a/apps/mobile/app/services/interfaces/ITask.ts
+++ b/apps/mobile/app/services/interfaces/ITask.ts
@@ -17,6 +17,7 @@ export type ITeamTask = {
estimateDays?: number
estimateHours?: number
estimateMinutes?: number
+ startDate?: string
dueDate: string
projectId: string
public: boolean
diff --git a/apps/mobile/app/services/interfaces/ITaskVersion.ts b/apps/mobile/app/services/interfaces/ITaskVersion.ts
new file mode 100644
index 000000000..62653e8a2
--- /dev/null
+++ b/apps/mobile/app/services/interfaces/ITaskVersion.ts
@@ -0,0 +1,28 @@
+export interface ITaskVersionItemList {
+ id: string
+ createdAt: string
+ updatedAt: string
+ tenantId: string
+ organizationId: string
+ name?: string
+ value?: string
+ description?: string
+ icon?: string
+ fullIconUrl?: string
+ color?: string
+ is_system?: boolean
+ isSystem?: boolean
+ projectId?: string
+}
+
+export interface ITaskVersionCreate {
+ name: string
+ description?: string
+ icon?: string
+ color?: string
+ projectId?: string
+ organizationId?: string
+ tenantId?: string | undefined | null
+ organizationTeamId?: string | undefined | null
+ value?: string
+}
diff --git a/apps/mobile/package.json b/apps/mobile/package.json
index b69178a00..38ff3efcf 100644
--- a/apps/mobile/package.json
+++ b/apps/mobile/package.json
@@ -82,6 +82,7 @@
"react-native": "0.71.8",
"react-native-animatable": "^1.3.3",
"react-native-bootsplash": "4.5.3",
+ "react-native-calendars": "^1.1302.0",
"react-native-circular-progress": "^1.3.8",
"react-native-dotenv": "^3.4.8",
"react-native-dropdown-picker": "^5.4.6",
diff --git a/apps/mobile/yarn.lock b/apps/mobile/yarn.lock
index 7bd0935e4..833bae03c 100644
--- a/apps/mobile/yarn.lock
+++ b/apps/mobile/yarn.lock
@@ -7761,7 +7761,7 @@ hoek@6.x.x:
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c"
integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==
-hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
+hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -9554,7 +9554,7 @@ lodash-es@^4.2.1:
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
-lodash.debounce@^4.0.8:
+lodash.debounce@4.0.8, lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
@@ -9778,7 +9778,7 @@ memfs@^3.4.3:
dependencies:
fs-monkey "^1.0.3"
-memoize-one@^5.0.0:
+memoize-one@^5.0.0, memoize-one@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
@@ -12146,7 +12146,7 @@ prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2, prompts@^2.4.0:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@*, prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@*, prop-types@15.8.1, prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -12459,6 +12459,21 @@ react-native-bootsplash@4.5.3:
picocolors "^1.0.0"
sharp "^0.31.3"
+react-native-calendars@^1.1302.0:
+ version "1.1302.0"
+ resolved "https://registry.yarnpkg.com/react-native-calendars/-/react-native-calendars-1.1302.0.tgz#1b81074d08a9aa5aadcd2fb546d08517d4974952"
+ integrity sha512-QZdkFYVKafxjc/oHmbmzyEhMkF0sWl+1hYd9FbKQFcf/c3D0K+sfG81A40C1YsOR8nxb1nq2OpsNT81CGd1L4Q==
+ dependencies:
+ hoist-non-react-statics "^3.3.1"
+ lodash "^4.17.15"
+ memoize-one "^5.2.1"
+ prop-types "^15.5.10"
+ react-native-swipe-gestures "^1.0.5"
+ recyclerlistview "^4.0.0"
+ xdate "^0.8.0"
+ optionalDependencies:
+ moment "^2.29.4"
+
react-native-circular-progress@^1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/react-native-circular-progress/-/react-native-circular-progress-1.3.8.tgz#84713b42286e4778aaaeecd7910953bb9de018ce"
@@ -12583,6 +12598,11 @@ react-native-svg@13.4.0:
css-select "^5.1.0"
css-tree "^1.1.3"
+react-native-swipe-gestures@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz#a172cb0f3e7478ccd681fd36b8bfbcdd098bde7c"
+ integrity sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw==
+
react-native-tab-view@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-3.5.2.tgz#2789b8af6148b16835869566bf13dc3b0e6c1b46"
@@ -12840,6 +12860,15 @@ recast@^0.20.4:
source-map "~0.6.1"
tslib "^2.0.1"
+recyclerlistview@^4.0.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/recyclerlistview/-/recyclerlistview-4.2.0.tgz#a140149aaa470c9787a1426452651934240d69ef"
+ integrity sha512-uuBCi0c+ggqHKwrzPX4Z/mJOzsBbjZEAwGGmlwpD/sD7raXixdAbdJ6BTcAmuWG50Cg4ru9p12M94Njwhr/27A==
+ dependencies:
+ lodash.debounce "4.0.8"
+ prop-types "15.8.1"
+ ts-object-utils "0.0.5"
+
redux-logger@^2.7.4:
version "2.10.2"
resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-2.10.2.tgz#3c5a5f0a6f32577c1deadf6655f257f82c6c3937"
@@ -14694,6 +14723,11 @@ ts-jest@^29.1.0:
semver "^7.5.3"
yargs-parser "^21.0.1"
+ts-object-utils@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/ts-object-utils/-/ts-object-utils-0.0.5.tgz#95361cdecd7e52167cfc5e634c76345e90a26077"
+ integrity sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA==
+
tsconfig-paths@^3.14.1:
version "3.14.2"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088"
@@ -15470,6 +15504,11 @@ xcode@3.0.1, xcode@^3.0.0, xcode@^3.0.1:
simple-plist "^1.1.0"
uuid "^7.0.3"
+xdate@^0.8.0:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/xdate/-/xdate-0.8.2.tgz#d7b033c00485d02695baf0044f4eacda3fc961a3"
+ integrity sha512-sNBlLfOC8S3V0vLDEUianQOXcTsc9j4lfeKU/klHe0RjHAYn0CXsSttumTot8dzalboV8gZbH38B+WcCIBjhFQ==
+
xdl@^51.5.0:
version "51.6.4"
resolved "https://registry.yarnpkg.com/xdl/-/xdl-51.6.4.tgz#32abaf71be10426f81a98a43b0def2f39ad4590e"