diff --git a/.eslintrc.js b/.eslintrc.js index 7801d47..063a319 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,5 +26,9 @@ module.exports = { rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + "react/prop-types": "off" + } + + } diff --git a/TestServer/cmd/main.go b/TestServer/cmd/main.go new file mode 100644 index 0000000..fd1f3ab --- /dev/null +++ b/TestServer/cmd/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "encoding/json" + "net/http" + "strconv" +) + +type Task struct { + ID int `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Completed bool `json:"completed"` +} + +var tasks = []Task{ + {ID: 1, Title: "Task 1", Description: "Description of Task 1", Completed: false}, + {ID: 2, Title: "Task 2", Description: "Description of Task 2", Completed: true}, + {ID: 3, Title: "Task 3", Description: "Description of Task 3", Completed: false}, +} + +func getTasksHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(tasks) +} + +func main() { + http.HandleFunc("/api/tasks", getTasksHandler) + + port := 8080 + println("Server is running on port " + strconv.Itoa(port)) + http.ListenAndServe(":"+strconv.Itoa(port), nil) +} diff --git a/TestServer/go.mod b/TestServer/go.mod new file mode 100644 index 0000000..c100e48 --- /dev/null +++ b/TestServer/go.mod @@ -0,0 +1,3 @@ +module moxitech/todo/server + +go 1.22.7 diff --git a/package-lock.json b/package-lock.json index 3a6128b..4d8728d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@types/react-dom": "^18.0.6", "@types/react-router": "^5.1.11", "@types/react-router-dom": "^5.1.7", + "axios": "^1.7.7", "history": "^4.9.0", "ionicons": "^7.0.0", "react": "^18.2.0", @@ -4845,6 +4846,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -13058,6 +13082,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index db446a0..3f64e2a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@types/react-dom": "^18.0.6", "@types/react-router": "^5.1.11", "@types/react-router-dom": "^5.1.7", + "axios": "^1.7.7", "history": "^4.9.0", "ionicons": "^7.0.0", "react": "^18.2.0", diff --git a/src/components/ActionButton/ModalAdd.tsx b/src/components/ActionButton/ModalAdd.tsx new file mode 100644 index 0000000..a3b6db2 --- /dev/null +++ b/src/components/ActionButton/ModalAdd.tsx @@ -0,0 +1,56 @@ +import React, { useState } from 'react'; +import { IonButtons, IonButton, IonModal, IonHeader, IonContent, IonToolbar, IonTitle, IonPage, IonInput, IonIcon } from '@ionic/react'; +import { close } from 'ionicons/icons'; + +interface ModalAddProps { + isOpen: boolean; + setIsOpen: (isOpen: boolean) => void; + saveTask: (task: Task) => void; +} + +interface Task { + id?: number; + title: string; + description?: string; + completed?: boolean; +} + +const ModalAdd: React.FC = ({ isOpen, setIsOpen, saveTask }) => { + const [taskTitle, setTaskTitle] = useState(''); + + const handleSave = () => { + if (taskTitle.trim()) { + saveTask({ title: taskTitle }); + setTaskTitle(''); + setIsOpen(false); + } + }; + + return ( + setIsOpen(false)}> + + + Add new task + + setIsOpen(false)}> + + + + + + + setTaskTitle(e.detail.value!)} + /> + + Save Task + + + + ); +}; + +export default ModalAdd; \ No newline at end of file diff --git a/src/components/ExploreContainer.css b/src/components/ExploreContainer.css index c2c47cf..d3c9ade 100644 --- a/src/components/ExploreContainer.css +++ b/src/components/ExploreContainer.css @@ -21,4 +21,9 @@ #container a { text-decoration: none; +} + + +.color { + background-color: #2dd55b; } \ No newline at end of file diff --git a/src/components/ExploreContainer.tsx b/src/components/ExploreContainer.tsx index 4e009f7..ebac442 100644 --- a/src/components/ExploreContainer.tsx +++ b/src/components/ExploreContainer.tsx @@ -1,13 +1,43 @@ -import React from 'react'; +import React, { useState } from 'react'; import './ExploreContainer.css'; +import ModalAdd from './ActionButton/ModalAdd'; +import { IonFab, IonFabButton, IonIcon } from '@ionic/react'; +import { add } from 'ionicons/icons'; +import { useTaskStorage } from '../service/TaskLoader'; const ExploreContainer: React.FC = () => { + const [isOpen, setIsOpen] = useState(false); + const { + tasks, + loading, + saveTask, + deleteTask + } = useTaskStorage(); + return ( -
- Ready to create an app? -

Start with Ionic UI Components

+
+ {loading ? ( +

Loading...

+ ) : ( +
    + {tasks.map((task, index) => ( +
  • {task.title}
  • + ))} +
+ )} + + + + setIsOpen(true)}> + + +
); }; -export default ExploreContainer; +export default ExploreContainer; \ No newline at end of file diff --git a/src/pages/Home.css b/src/pages/Home.css index e69de29..82dfe75 100644 --- a/src/pages/Home.css +++ b/src/pages/Home.css @@ -0,0 +1,6 @@ + + + +.color-green { + color: rgb(41, 228, 41); +} \ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index cf9bd29..927400e 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -8,16 +8,16 @@ const Home: React.FC = () => { - Blank + Moxitech ToDo - Blank + Moxitech TODO - + ); diff --git a/src/service/TaskLoader.ts b/src/service/TaskLoader.ts new file mode 100644 index 0000000..41b1446 --- /dev/null +++ b/src/service/TaskLoader.ts @@ -0,0 +1,68 @@ +import { useEffect, useState } from 'react'; + + +interface Task { + id?: number; + title: string; + description?: string; + completed?: boolean; +} +export const useTaskStorage = () => { + const [tasks, setTasks] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + loadTasks(); + }, []); + + const loadTasks = () => { + setLoading(true); + try { + const storedTasks = localStorage.getItem('tasks'); + if (storedTasks) { + setTasks(JSON.parse(storedTasks)); + } + } catch (error) { + console.error('Ошибка при загрузке задач:', error); + } finally { + setLoading(false); + } + }; + + const saveTask = (task: Task) => { + try { + if (task.id) { + // Update existing task + setTasks(prevTasks => { + const updatedTasks = prevTasks.map(t => (t.id === task.id ? task : t)); + localStorage.setItem('tasks', JSON.stringify(updatedTasks)); + return updatedTasks; + }); + } else { + // Create new task + setTasks(prevTasks => { + const newTask = { ...task, id: Date.now() }; + const updatedTasks = [...prevTasks, newTask]; + localStorage.setItem('tasks', JSON.stringify(updatedTasks)); + return updatedTasks; + }); + } + } catch (error) { + console.error('Ошибка при сохранении задачи:', error); + } + }; + + const deleteTask = (taskId: number) => { + try { + setTasks(prevTasks => { + const updatedTasks = prevTasks.filter(task => task.id !== taskId); + localStorage.setItem('tasks', JSON.stringify(updatedTasks)); + return updatedTasks; + }); + } catch (error) { + console.error('Ошибка при удалении задачи:', error); + } + }; + + return { tasks, loading, loadTasks, saveTask, deleteTask }; +}; \ No newline at end of file