Skip to content

Commit

Permalink
feat: implement basic semi-interactive task board
Browse files Browse the repository at this point in the history
  • Loading branch information
rinczefi-tp committed Sep 11, 2024
1 parent ab5b67f commit 15be811
Show file tree
Hide file tree
Showing 23 changed files with 6,540 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
40 changes: 40 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Deploy to GitHub Pages

on:
push:
branches:
- main

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
buildAndDeploy:
runs-on: ubuntu-latest

name: Build and deploy

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Install dependencies
run: yarn

- name: Set up Git user config
run: |
git config user.email "[email protected]"
git config user.name "GitHub Actions"
# TODO: Check if it can be solved otherwise
# Maybe it's because the SSH connection (try to use github-personal.com instead of github.com)
- name: Set Remote URL with token
run: git remote set-url origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}"

- name: Build and deploy project
run: yarn deploy
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// jest.config.js
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
testMatch: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[tj]s?(x)"],
};
43 changes: 43 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "rinczefi-cv",
"version": "1.0.0",
"description": "My biography written in React.js (TS)",
"main": "index.js",
"scripts": {
"start": "webpack serve --mode development",
"build": "webpack --mode production",
"test": "jest",
"predeploy": "yarn build",
"deploy": "gh-pages -d dist"
},
"author": "Inczefi Robert",
"license": "MIT",
"dependencies": {
"@emotion/react": "11.13.3",
"@emotion/styled": "11.13.0",
"@hello-pangea/dnd": "16.6.0",
"@mui/material": "6.0.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"typescript": "5.5.4"
},
"devDependencies": {
"@babel/core": "7.25.2",
"@babel/preset-env": "7.25.4",
"@babel/preset-react": "7.24.7",
"@types/jest": "29.5.12",
"@types/react": "18.3.5",
"@types/react-dom": "18.3.0",
"babel-loader": "9.1.3",
"css-loader": "7.1.2",
"gh-pages": "6.1.1",
"html-webpack-plugin": "5.6.0",
"jest": "29.7.0",
"style-loader": "4.0.0",
"ts-jest": "29.2.5",
"ts-loader": "9.5.1",
"webpack": "5.94.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.0.4"
}
}
11 changes: 11 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Inczefi Robert CV</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
8 changes: 8 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from "react";
import ScrumBoard from "./components/ScrumBoard";

const App: React.FC = () => {
return <ScrumBoard />;
};

export default App;
39 changes: 39 additions & 0 deletions src/components/Column.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// src/components/Column.tsx
import React from "react";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import { Droppable } from "@hello-pangea/dnd";
import TaskCard from "./TaskCard";
import type { Task } from "../types";

interface Props {
title: string;
tasks: Task[];
columnId: string;
}

const Column: React.FC<Props> = ({ title, tasks, columnId }) => {
return (
<Paper sx={{ width: 300, padding: 2, margin: 1 }}>
<Typography variant="h5" align="center" gutterBottom>
{title}
</Typography>
<Droppable droppableId={columnId}>
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={{ minHeight: 100 }}
>
{tasks.map((task, index) => (
<TaskCard key={task.id} task={task} index={index} />
))}
{provided.placeholder}
</div>
)}
</Droppable>
</Paper>
);
};

export default Column;
47 changes: 47 additions & 0 deletions src/components/ScrumBoard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// src/components/ScrumBoard.tsx
import React, { useState } from "react";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid2";
import { DragDropContext, type DropResult } from "@hello-pangea/dnd";
import Column from "./Column";
import { type Task, TaskStatus } from "../types";
import { initialTasks } from "../data/tasks";
import { getTasksByStatus, getUpdatedTasks } from "../helpers";

const ScrumBoard: React.FC = () => {
const [tasks, setTasks] = useState<Task[]>(initialTasks);

const onDragEnd = (result: DropResult) => {
const updatedTasks = getUpdatedTasks(tasks, result);

if (updatedTasks) {
setTasks(updatedTasks);
}
};

return (
<DragDropContext onDragEnd={onDragEnd}>
<Box sx={{ flexGrow: 1, padding: 2 }}>
<Grid container spacing={2} justifyContent="center">
<Column
title={TaskStatus.ToDo}
tasks={getTasksByStatus(tasks, TaskStatus.ToDo)}
columnId={TaskStatus.ToDo}
/>
<Column
title={TaskStatus.InProgress}
tasks={getTasksByStatus(tasks, TaskStatus.InProgress)}
columnId={TaskStatus.InProgress}
/>
<Column
title={TaskStatus.Done}
tasks={getTasksByStatus(tasks, TaskStatus.Done)}
columnId={TaskStatus.Done}
/>
</Grid>
</Box>
</DragDropContext>
);
};

export default ScrumBoard;
40 changes: 40 additions & 0 deletions src/components/TaskCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// src/components/TaskCard.tsx
import React from "react";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Typography from "@mui/material/Typography";
import { Draggable } from "@hello-pangea/dnd";
import type { Task } from "../types";

interface Props {
task: Task;
index: number; // Required for draggable item
}

const TaskCard: React.FC<Props> = ({ task, index }) => {
return (
<Draggable draggableId={task.id.toString()} index={index}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={{ marginBottom: "8px", ...provided.draggableProps.style }}
>
<Card>
<CardContent>
<Typography variant="h6" component="div">
{task.title}
</Typography>
<Typography variant="body2" color="text.secondary">
{task.description}
</Typography>
</CardContent>
</Card>
</div>
)}
</Draggable>
);
};

export default TaskCard;
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ScrumBoard } from "./ScrumBoard";
46 changes: 46 additions & 0 deletions src/data/tasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { type Task, TaskStatus } from "../types";

export const initialTasks: Task[] = [
{
id: 1,
title: "Task 1",
description: "Description for Task 1",
status: TaskStatus.ToDo,
},
{
id: 2,
title: "Task 2",
description: "Description for Task 2",
status: TaskStatus.ToDo,
},
{
id: 3,
title: "Task 3",
description: "Description for Task 3",
status: TaskStatus.InProgress,
},
{
id: 4,
title: "Task 4",
description: "Description for Task 4",
status: TaskStatus.InProgress,
},
{
id: 5,
title: "Task 5",
description: "Description for Task 5",
status: TaskStatus.InProgress,
},
{
id: 6,
title: "Task 6",
description: "Description for Task 6",
status: TaskStatus.Done,
},
{
id: 7,
title: "Task 7",
description: "Description for Task 7",
status: TaskStatus.Done,
},
];
40 changes: 40 additions & 0 deletions src/helpers/__tests__/getTasksByStatus.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import getTasksByStatus from "../getTasksByStatus";
import { type Task, TaskStatus } from "../../types";
import {
doneTask,
firstToDoTask,
inProgressTask,
secondToDoTask,
} from "../mocks/tasks";

describe("getTasksByStatus", () => {
const initialTasks: Task[] = [
firstToDoTask,
secondToDoTask,
inProgressTask,
doneTask,
];

it("should return tasks with status 'To Do'", () => {
const result = getTasksByStatus(initialTasks, TaskStatus.ToDo);
expect(result).toEqual([firstToDoTask, secondToDoTask]);
});

it("should return tasks with status 'In Progress'", () => {
const result = getTasksByStatus(initialTasks, TaskStatus.InProgress);
expect(result).toEqual([inProgressTask]);
});

it("should return tasks with status 'Done'", () => {
const result = getTasksByStatus(initialTasks, TaskStatus.Done);
expect(result).toEqual([doneTask]);
});

it("should return an empty array if no tasks match the status", () => {
const result = getTasksByStatus(
initialTasks,
"Invalid Status" as Task["status"]
);
expect(result).toEqual([]);
});
});
Loading

0 comments on commit 15be811

Please sign in to comment.