diff --git a/apps/new/widget/Index.jsx b/apps/new/widget/Index.jsx index 024f5c08..3fe06f53 100644 --- a/apps/new/widget/Index.jsx +++ b/apps/new/widget/Index.jsx @@ -36,6 +36,14 @@ const config = { name: "Projects", }, }, + project: { + path: "${config_account}/widget/page.project.Index", + blockHeight: "final", + init: { + name: "Project", + }, + hide: true, + }, resources: { path: "${config_account}/widget/page.resources.Index", blockHeight: "final", diff --git a/apps/new/widget/components/project/Card.jsx b/apps/new/widget/components/project/Card.jsx index 9482422b..52cb40d7 100644 --- a/apps/new/widget/components/project/Card.jsx +++ b/apps/new/widget/components/project/Card.jsx @@ -111,7 +111,7 @@ const ProjectCard = ({ data, variant }) => { return ( <>, +}; +const config = { + theme: {}, + layout: { + src: "${alias_devs}/widget/Layout", + props: { + variant: "sidebar", + }, + }, + blocks: { + // these get passed to the layout and children + Header: () => <>, + Sidebar: () => ( + + ), + Footer: () => <>, + }, + router: { + param: "tab", + routes: { + myProjects: { + init: { + name: "My Projects", + icon: "bi bi-star", + }, + }, + myToolkits: { + init: { + name: "My Toolkits", + icon: "bi bi-database", + }, + }, + projectsInvolved: { + init: { + name: "Projects Involved", + icon: "bi bi-clipboard", + }, + }, + project: { + path: "${config_account}/widget/page.project.Main", + blockHeight: "final", + default: true, + hide: true, + }, + }, + }, +}; + +return ( +
+ +
+); diff --git a/apps/new/widget/page/project/Layout.jsx b/apps/new/widget/page/project/Layout.jsx new file mode 100644 index 00000000..591c3470 --- /dev/null +++ b/apps/new/widget/page/project/Layout.jsx @@ -0,0 +1,118 @@ +const { Metadata } = VM.require( + "${config_account}/widget/page.project.Metadata", +) || { + Metadata: () => <>, +}; + +const { href } = VM.require("${alias_old}/widget/lib.url") || { + href: () => {}, +}; +const Layout = ({ + projectAccountId, + profile, + routes, + children, + project, + id, + tab, +}) => { + const { title } = project; + + if (!projectAccountId) { + return

No Account ID

; + } + + const Nav = styled.div` + .nav-pills { + background: var(--bg-1, #0b0c14); + font-weight: 500; + --bs-nav-pills-border-radius: 0; + --bs-nav-link-color: var(--font-color, #fff); + --bs-nav-pills-link-active-color: var(--font-color, #fff); + --bs-nav-pills-link-active-bg: var(--bg-1, #0b0c14); + --bs-nav-link-padding-y: 0.75rem; + border-bottom: 1px solid var(--stroke-color, rgba(255, 255, 255, 0.2)); + padding-top: 3px; + } + .nav-link.active { + border-bottom: 2px solid var(--Yellow, #ffaf51); + } + + .nav-item:not(:has(> .disabled)):hover { + background: rgba(13, 110, 253, 0.15); + } + `; + return ( + <> +
+ + + Back to Projects + + +
+ + +
+
+ {children} +
+
+ + ); +}; +return { Layout }; diff --git a/apps/new/widget/page/project/Main.jsx b/apps/new/widget/page/project/Main.jsx new file mode 100644 index 00000000..d3a23276 --- /dev/null +++ b/apps/new/widget/page/project/Main.jsx @@ -0,0 +1,126 @@ +const { id } = props; + +const data = JSON.parse(Social.get(id, "final") ?? {}); +if (!id || !data) { + return "Loading..."; +} + +const profileData = { + name: data.title, + description: data.description, + linktree: { + github: data.github, + telegram: data.telegram, + twitter: data.twitter, + website: data.website, + }, + backgroundImage: data.backgroundImage?.image ?? data.backgroundImage, + image: data.profileImage?.image ?? data.profileImage, +}; + +const profile = Social.getr(`${data.projectAccountId}/profile`); + +const { Layout } = VM.require( + "${config_account}/widget/page.project.Layout", +) || { + Layout: () => <>, +}; +const config = { + theme: {}, + layout: { + src: "${alias_devs}/widget/Layout", + props: { + variant: "standard", + }, + }, + blocks: { + // these get passed to the layout and children + Header: () => ( + <> + + + ), + Sidebar: () => ( + + ), + Footer: () => <>, + }, + router: { + param: "tab", + routes: { + overview: { + path: "${config_account}/widget/page.project.tabs.Overview", + blockHeight: "final", + init: { + ...props, + }, + default: "true", + }, + activity: { + path: "${config_account}/widget/page.project.tabs.Discussion", + blockHeight: "final", + init: { + ...props, + }, + }, + discussion: { + path: "${config_account}/widget/page.project.tabs.Discussion", + blockHeight: "final", + init: { + ...props, + }, + }, + tasks: { + path: "${config_account}/widget/page.project.tabs.Task", + blockHeight: "final", + init: { + ...props, + }, + }, + code: { + path: "${config_account}/widget/page.project.tabs.Code", + blockHeight: "final", + init: { + ...props, + }, + }, + roadmap: { + path: "${config_account}/widget/page.project.tabs.Roadmap", + blockHeight: "final", + init: { + ...props, + }, + }, + }, + }, +}; + +// remove unselected tabs +if (Array.isArray(data?.tabs)) { + Object.keys(config.router.routes).forEach((key) => { + if (!data.tabs.includes(key)) { + delete config.router.routes[key]; + } + }); +} + +return ( +
+ +
+); diff --git a/apps/new/widget/page/project/Metadata.jsx b/apps/new/widget/page/project/Metadata.jsx new file mode 100644 index 00000000..a5a01947 --- /dev/null +++ b/apps/new/widget/page/project/Metadata.jsx @@ -0,0 +1,125 @@ +const BackgroundImage = styled.div` + img { + height: 252px; + } + + @media screen and (max-width: 768px) { + img { + height: 126px; + } + } +`; + +const ProfileInfo = styled.div` + display: flex; + align-items: flex-start; + flex-direction: row; + gap: 24px; + + .left { + img { + width: 100px; + height: 100px; + border-radius: 100px; + } + } + + @media screen and (max-width: 768px) { + .left { + img { + width: 64px; + height: 64px; + } + } + } + + .right { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 24px; + + .info { + display: flex; + align-items: flex-start; + gap: 4px; + flex-direction: column; + h3 { + color: var(--white-100, #fff); + font-size: 24px; + font-weight: 500; + margin: 0; + } + + p { + color: var(--white-50, #b0b0b0); + font-size: 16px; + margin: 0; + } + } + + .links { + color: var(--white-100, #fff); + font-size: 13px; + + display: flex; + flex-direction: column; + gap: 8px; + } + } +`; +const Metadata = ({ profile, title, projectAccountId }) => { + return ( +
+ {" "} + + {profile.backgroundImage && ( + + )} + + +
+ +
+
+
+

{title ?? profile.name}

+

@{projectAccountId}

+
+ +
+ Links + +
+
+
+
+ ); +}; + +return { Metadata }; diff --git a/apps/new/widget/page/project/tabs/Code.jsx b/apps/new/widget/page/project/tabs/Code.jsx new file mode 100644 index 00000000..b4da66b0 --- /dev/null +++ b/apps/new/widget/page/project/tabs/Code.jsx @@ -0,0 +1,30 @@ +const { Button } = VM.require("${alias_old}/widget/components") || { + Button: () => <>, +}; + +const { getProjectMeta } = VM.require( + "${alias_old}/widget/lib.project-data", +) || { + getProjectMeta: () => {}, +}; + +const { id } = props; + +const project = getProjectMeta(id); + +const { gitHub } = project; +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + align-items: center; + justify-content: center; +`; + +return ( + + + +); diff --git a/apps/new/widget/page/project/tabs/Discussion.jsx b/apps/new/widget/page/project/tabs/Discussion.jsx new file mode 100644 index 00000000..3f760881 --- /dev/null +++ b/apps/new/widget/page/project/tabs/Discussion.jsx @@ -0,0 +1,69 @@ +const { Feed } = VM.require("${alias_devs}/widget/Feed") ?? { + Feed: () => <>, +}; +const { Post } = VM.require("${alias_old}/widget/components") || { + Post: () => <>, +}; +const { getProjectMeta } = VM.require( + "${alias_old}/widget/lib.project-data", +) || { + getProjectMeta: () => {}, +}; + +const { id } = props; + +const project = getProjectMeta(id); +const { projectAccountId } = project; + +return ( +
+
+ } + src="${alias_old}/widget/Compose" + props={{ + draftKey: id + "_discussions", + }} + /> + ( + + )} + /> + +); diff --git a/apps/new/widget/page/project/tabs/Overview.jsx b/apps/new/widget/page/project/tabs/Overview.jsx new file mode 100644 index 00000000..75142cb6 --- /dev/null +++ b/apps/new/widget/page/project/tabs/Overview.jsx @@ -0,0 +1,110 @@ +const { User, Hashtag } = VM.require("${alias_old}/widget/components") || { + User: () => <>, + Hashtag: () => <>, +}; + +const { getProjectMeta } = VM.require( + "${alias_old}/widget/lib.project-data", +) || { + getProjectMeta: () => {}, +}; + +const { id } = props; + +const project = getProjectMeta(id); + +const { description, tags, contributors, accountId, location, teamSize } = + project; + +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + + .section { + display: flex; + flex-direction: column; + gap: 8px; + } + + .heading { + color: var(--white-100, #fff); + line-height: 170%; /* 27.2px */ + margin: 0; + } + + .description { + color: var(--white-50, #b0b0b0); + line-height: 170%; /* 27.2px */ + margin: 0; + } +`; + +const MapIcon = () => ( + + + + + + + + + + +); + +return ( + +
+

About

+

+ {description ? ( + + ) : ( + "No information available" + )} +

+
+
+
+

Location

+

+ {location ?? "No Location"} +

+
+
+

Team Size

+

+ + {teamSize || "unspecified"} +

+
+
+
+

Contributors

+ {!contributors &&

No Contributors

} +
+ {contributors && + contributors.map((teammate) => ( + + ))} +
+
+
+

Project Tags

+
+ {tags && tags.map((it) => {it})} + {tags.length === 0 &&

No tags

} +
+
+
+); diff --git a/apps/new/widget/page/project/tabs/Roadmap.jsx b/apps/new/widget/page/project/tabs/Roadmap.jsx new file mode 100644 index 00000000..5155d5cc --- /dev/null +++ b/apps/new/widget/page/project/tabs/Roadmap.jsx @@ -0,0 +1,28 @@ +const { Button } = VM.require("${alias_old}/widget/components") || { + Button: () => <>, +}; + +const { getProjectMeta } = VM.require( + "${alias_old}/widget/lib.project-data", +) || { + getProjectMeta: () => {}, +}; + +const { id } = props; + +const project = getProjectMeta(id); + +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + justify-content: center; + .link { + display: flex; + gap: 16px; + flex-direction: row; + align-items: center; + } +`; + +return ; diff --git a/apps/new/widget/page/project/tabs/Roles.jsx b/apps/new/widget/page/project/tabs/Roles.jsx new file mode 100644 index 00000000..42875683 --- /dev/null +++ b/apps/new/widget/page/project/tabs/Roles.jsx @@ -0,0 +1 @@ +return
Roles
; diff --git a/apps/new/widget/page/project/tabs/Task.jsx b/apps/new/widget/page/project/tabs/Task.jsx new file mode 100644 index 00000000..b08821d0 --- /dev/null +++ b/apps/new/widget/page/project/tabs/Task.jsx @@ -0,0 +1,840 @@ +const { Modal, Button, ProgressState } = VM.require( + "${alias_old}/widget/components", +) || { + Modal: () => <>, + Button: () => <>, + ProgressState: () => <>, +}; + +const { normalize } = VM.require("${alias_devs}/widget/lib.stringUtils") || { + normalize: () => {}, +}; + +const { getProjectMeta } = VM.require( + "${alias_old}/widget/lib.project-data", +) || { + getProjectMeta: () => {}, +}; + +const { id } = props; + +const project = getProjectMeta(id); +const app = props.app || "${alias_old}"; +const type = props.type || "task"; +const projectTask = "project-task"; + +const ThemeContainer = + props.ThemeContainer || + styled.div` + --primary-color: rgb(255, 175, 81); + --border-color: rgba(255, 255, 255, 0.2); + --font-color: #fff; + --menu-bg-color: #0b0c14; + --secondary-font-color: rgba(176, 176, 176, 1); + --card-bg-color: rgba(35, 36, 43, 1); + `; + +const Wrapper = styled.div` + color: white; + + .border { + border-color: var(--border-color) !important; + } + input::placeholder { + color: var(--secondary-font-color) !important; + } + + .form-control { + background: #23242b !important; + color: #fff !important; + border: 1px solid var(--border-color) !important; + } + + .form-check-input:checked { + background-color: var(--primary-color) !important; + border-color: var(--primary-color) !important; + } + + .cbx:hover span:first-child { + border-color: var(--primary-color) !important; + } + + .pointer { + cursor: pointer; + } + + .red { + color: #dc3545; + } + + .badge { + border: 1px solid var(--primary-color) !important; + } + + .hashtag { + color: var(--primary-color) !important; + } + + .secondary-text { + color: var(--secondary-font-color) !important; + } + + .dropdown-menu { + background-color: var(--menu-bg-color) !important; + color: var(--font-color) !important; + + li.dropdown-item { + display: flex; + gap: 10px; + align-items: center; + cursor: pointer; + color: var(--font-color) !important; + &:hover { + a { + color: var(--menu-bg-color) !important; + } + } + } + + .link-dark, + .dropdown-item { + color: var(--font-color) !important; + + &:hover { + color: var(--menu-bg-color) !important; + + span { + color: var(--menu-bg-color) !important; + } + } + } + + .dropdown-item.active, + .dropdown-item:active { + background-color: var(--primary-color) !important; + } + } + + .container { + border: none !important; + } + + .assignee-item { + display: inline-block; + padding: 0.2em 0.4em; + border-radius: 10px; + border: 0.8px solid lightgray; + position: relative; + } + + .flex-1 { + flex: 1; + } +`; + +const projectID = normalize(project?.title, "-"); + +const StatusValues = { + PROPOSED: "proposed", + PROGRESS: "progress", + COMPLETED: "completed", +}; + +const listItem = { title: "", isCompleted: false }; +const task = { + title: "", + description: "", + author: context.accountId, + tags: [], + list: [], // listItem + status: "", + priority: "", + assignees: [], + startDate: "", + endDate: "", +}; + +const [proposedTasks, setProposedTasks] = useState([]); +const [progressTasks, setProgresTasks] = useState([]); +const [completedTasks, setCompletedTasks] = useState([]); +const [showAddTaskModal, setShowAddTaskModal] = useState(false); +const [taskDetail, setTaskDetail] = useState({}); +const [showDropdownIndex, setShowDropdownIndex] = useState(null); +const [isEditTask, setIsEdit] = useState(false); +const [showDeleteConfirmationModalIndex, setDeleteConfirmationIndex] = + useState(null); +const [showViewTaskModal, setViewTaskModal] = useState(false); +const [currentEditTaskId, setCurrentTaskId] = useState(null); // if user change title we need the same earlier key to update the data + +const isAllowedToEdit = (project.contributors ?? []).includes( + context.accountId, +); + +const flattenObject = (obj) => { + let paths = []; + + try { + Object.keys(obj).forEach((key) => { + const projects = Object.keys( + obj?.[key]?.[app]?.[projectTask]?.[projectID]?.[type] ?? {}, + ); + projects.map((path) => { + if (!path || !path.includes("_")) { + return; + } + const convertedStr = path.replace(/_/g, "/"); + paths.push(convertedStr); + }); + }); + } catch (e) {} + return paths; +}; + +const processData = useCallback( + (data) => { + const accounts = Object.entries(data ?? {}); + const allTasks = accounts + .map((account) => { + return Object.entries(account?.[1]?.[type] ?? {}).map((kv) => { + const metadata = JSON.parse(kv[1]); + return { + ...metadata, + oldTitle: kv[0], + }; + }); + }) + .flat(); + return allTasks; + }, + [type], +); + +function fetchTasks() { + if (!projectID) { + return; + } + const keys = Social.keys( + `*/${app}/${projectTask}/${projectID}/${type}/*`, + "final", + { + order: "desc", + subscribe: true, + }, + ); + if (!keys) { + return "Loading..."; + } + let flattenedKeys = flattenObject(keys); + + const data = Social.get(flattenedKeys, "final"); + // check if task is singular (since we have to update the return format for parsing) + const isSingular = flattenedKeys.length === 1; + if (isSingular) { + const [name, task, taskName] = flattenedKeys?.[0]?.split("/").slice(0, 3); + return { + [name]: { + [task]: { + [taskName]: data, + }, + }, + }; + } + return data; +} + +const data = fetchTasks(); +const tasks = processData(data); + +function sortByPriority(a, b) { + const priorityOrder = { P0: 0, P1: 1, P2: 2, P3: 3 }; + return priorityOrder[a.priority] - priorityOrder[b.priority]; +} + +useEffect(() => { + if (Array.isArray(tasks)) { + setProposedTasks( + tasks + .filter((i) => i.status === StatusValues.PROPOSED) + .sort(sortByPriority), + ); + setProgresTasks( + tasks + .filter((i) => i.status === StatusValues.PROGRESS) + .sort(sortByPriority), + ); + setCompletedTasks( + tasks + .filter((i) => i.status === StatusValues.COMPLETED) + .sort(sortByPriority), + ); + } +}, [tasks]); + +const updateTaskDetail = (data) => { + setTaskDetail((prevState) => ({ + ...prevState, + ...data, + })); +}; + +const updateTaskListItem = (index, updatedItem) => { + const updatedList = [...taskDetail.list]; + updatedList[index] = updatedItem; + updateTaskDetail({ list: updatedList }); +}; + +const deleteTaskListItem = (index) => { + const updatedList = [ + ...taskDetail.list.slice(0, index), + ...taskDetail.list.slice(index + 1), + ]; + updateTaskDetail({ list: updatedList }); +}; + +const onAddTask = () => { + const taskId = normalize(taskDetail.title, "-"); + const data = { + [type]: { + [taskId]: { + "": JSON.stringify(taskDetail), + metadata: taskDetail, + }, + }, + [app]: { + [projectTask]: { + [projectID]: { + [type]: { + [`${context.accountId}_task_${taskId}`]: "", + }, + }, + }, + }, + }; + Social.set(data, { + onCommit: () => setShowAddTaskModal(false), + }); +}; + +const onEditTask = useCallback( + (data) => { + const newData = data ?? taskDetail; + const taskId = currentEditTaskId; + const updatedData = { + [type]: { + [taskId]: { + "": JSON.stringify(newData), + metadata: newData, + }, + }, + [app]: { + [projectTask]: { + [projectID]: { + [type]: { + [`${context.accountId}_task_${taskId}`]: "", + }, + }, + }, + }, + }; + Social.set(updatedData, { + force: true, + onCommit: () => setShowAddTaskModal(false), + }); + }, + [taskDetail, currentEditTaskId], +); + +const onDeleteTask = useCallback(() => { + const taskId = currentEditTaskId; + const updatedData = { + [type]: { + [taskId]: null, + }, + [app]: { + [projectTask]: { + [projectID]: { + [type]: { + [`${context.accountId}_task_${taskId}`]: null, + }, + }, + }, + }, + }; + Social.set(updatedData, { + force: true, + }); +}, [taskDetail, currentEditTaskId]); + +function handleDropdownToggle(columnTitle, index, value) { + setShowDropdownIndex((prevState) => ({ + ...prevState, + [columnTitle + index]: value ?? !prevState[columnTitle + index] ?? true, + })); +} + +const DropdownMenu = ({ columnTitle, item, index, changeStatusOptions }) => { + return ( + event.stopPropagation()} + tabIndex="0" + onBlur={() => handleDropdownToggle(columnTitle, index, false)} + > + + {showDropdownIndex[columnTitle + index] && ( +
    +
  • { + handleDropdownToggle(columnTitle, index); + setIsEdit(true); + setShowAddTaskModal(true); + }} + > + Edit Task +
  • +
  • { + handleDropdownToggle(columnTitle, index); + setDeleteConfirmationIndex(index); + }} + > + Delete Task +
  • + {(changeStatusOptions ?? []).length > 0 && ( +
    +
    +
    + Change Status +
    + {changeStatusOptions.map((i) => ( +
  • { + const data = { status: i.value }; + updateTaskDetail(data); + handleDropdownToggle(columnTitle, index); + onEditTask({ ...taskDetail, ...data }); + }} + > + + {i.label} +
  • + ))} +
    + )} +
+ )} +
+ ); +}; + +const DeleteConfirmationModal = () => { + return ( + setDeleteConfirmationIndex(null)} + > +
+ Are you sure you want to delete the task ? +
+ + +
+
+
+ ); +}; + +const today = new Date().toISOString().split("T")[0]; + +const AddTaskModal = () => { + return ( + { + setShowAddTaskModal(!showAddTaskModal); + setTaskDetail(null); + }} + > +
+
+ + updateTaskDetail({ title: e.target.value })} + /> +
+
+ + updateTaskDetail({ description: e.target.value })} + /> +
+
+ + +
+
+ + { + const data = e.map((i) => (i.label ? i.label : i)); + updateTaskDetail({ assignees: data }); + }} + /> +
+
+ + { + const data = e.map((i) => (i.label ? i.label : i)); + updateTaskDetail({ tags: data }); + }} + /> +
+
+
+ + updateTaskDetail({ startDate: e.target.value })} + /> +
+ +
+ + updateTaskDetail({ endDate: e.target.value })} + /> +
+
+
+
+ +
+ updateTaskDetail({ + list: [...(taskDetail.list ?? []), { ...listItem }], + }) + } + > + +
+
+
+ {Array.isArray(taskDetail.list) && + taskDetail.list?.map((item, index) => ( +
+
+ + updateTaskListItem(index, { + title: item.title, + isCompleted: e.target.checked, + }) + } + /> + + updateTaskListItem(index, { + title: e.target.value, + isCompleted: false, + }) + } + /> +
deleteTaskListItem(index)}> + +
+
+
+ ))} +
+
+
+ {!isEditTask && ( + + )} + +
+
+
+ ); +}; + +function formatDate(date) { + return date; +} + +const ViewTaskModal = () => { + return ( + { + setViewTaskModal(!showViewTaskModal); + setTaskDetail(null); + }} + > +
+
+ +
{taskDetail.title}
+
+
+ +
{taskDetail.description}
+
+
+ +
{taskDetail.priority ?? "None"}
+
+
+ +
+ {Array.isArray(taskDetail.assignees) && + taskDetail.assignees.map((assignee) => ( +
+ +
+ ))} +
+
+
+ +
+ {Array.isArray(taskDetail.tags) && + taskDetail.tags.map((tag) => ( + + # + {tag} + + ))} +
+
+
+
+ +
+ {formatDate(taskDetail.startDate)} +
+
+ +
+ +
+ {formatDate(taskDetail.endDate)} +
+
+
+
+
+ +
+
+ {Array.isArray(taskDetail.list) && + taskDetail.list?.map((item) => ( +
+
+ + +
+
+ ))} +
+
+
+
+ ); +}; + +const Column = ({ title, addTask, columnTasks, changeStatusOptions }) => { + return ( +
+
+ {title} + {isAllowedToEdit && ( +
+ +
+ )} +
+
+ {columnTasks.map((item, index) => ( +
{ + setViewTaskModal(true); + setTaskDetail(item); + }} + style={{ backgroundColor: "var(--card-bg-color)" }} + className="p-3 d-flex justify-content-between rounded-2 gap-2 pointer" + > +
+
{item.title}
+
Author: {item.author}
+
Priority: {item.priority}
+ {/*
Last edited:
*/} +
+ {isAllowedToEdit && ( + + )} +
+ ))} +
+
+ ); +}; + +const columns = [ + { + title: "Proposed", + columnTasks: proposedTasks, + addTask: () => { + setTaskDetail({ ...task, status: StatusValues.PROPOSED }); + setShowAddTaskModal(true); + }, + changeStatusOptions: [ + { label: "In Progress", value: StatusValues.PROGRESS }, + ], + }, + { + title: "In Progress", + columnTasks: progressTasks, + addTask: () => { + setTaskDetail({ ...task, status: StatusValues.PROGRESS }); + setShowAddTaskModal(true); + }, + changeStatusOptions: [ + { label: "Completed", value: StatusValues.COMPLETED }, + ], + }, + { + title: "Completed", + columnTasks: completedTasks, + addTask: () => { + setTaskDetail({ ...task, status: StatusValues.COMPLETED }); + setShowAddTaskModal(true); + }, + changeStatusOptions: [], + }, +]; + +return ( + + + + + +
+
+ {columns.map((item) => ( + + ))} +
+
+
+
+); diff --git a/apps/old/widget/lib/project-data.jsx b/apps/old/widget/lib/project-data.jsx index a3386622..6e917de5 100644 --- a/apps/old/widget/lib/project-data.jsx +++ b/apps/old/widget/lib/project-data.jsx @@ -2,13 +2,13 @@ const getProjectMeta = (id) => { if (!id) { - throw new Error("Invalid project ID"); + console.log("Invalid project ID"); } const data = Social.get(id, "final"); if (!data) { - throw new Error("Failed to fetch project data"); + console.log("Failed to fetch project data"); } try {