This commit is contained in:
moxitech 2024-10-13 11:01:14 +07:00
parent abde6c9bed
commit 187f146766
11 changed files with 243 additions and 8 deletions

View File

@ -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"
}
}

33
TestServer/cmd/main.go Normal file
View File

@ -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)
}

3
TestServer/go.mod Normal file
View File

@ -0,0 +1,3 @@
module moxitech/todo/server
go 1.22.7

29
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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<ModalAddProps> = ({ isOpen, setIsOpen, saveTask }) => {
const [taskTitle, setTaskTitle] = useState('');
const handleSave = () => {
if (taskTitle.trim()) {
saveTask({ title: taskTitle });
setTaskTitle('');
setIsOpen(false);
}
};
return (
<IonModal isOpen={isOpen} onDidDismiss={() => setIsOpen(false)}>
<IonHeader>
<IonToolbar>
<IonTitle>Add new task</IonTitle>
<IonButtons slot="end">
<IonButton onClick={() => setIsOpen(false)}>
<IonIcon icon={close}></IonIcon>
</IonButton>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent className="ion-padding">
<IonInput
type='text'
value={taskTitle}
placeholder='Enter task'
onIonChange={(e) => setTaskTitle(e.detail.value!)}
/>
<IonButton expand="block" onClick={handleSave}>
Save Task
</IonButton>
</IonContent>
</IonModal>
);
};
export default ModalAdd;

View File

@ -21,4 +21,9 @@
#container a {
text-decoration: none;
}
.color {
background-color: #2dd55b;
}

View File

@ -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 (
<div id="container">
<strong>Ready to create an app?</strong>
<p>Start with Ionic <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
<div>
{loading ? (
<p>Loading...</p>
) : (
<ul>
{tasks.map((task, index) => (
<li key={index}>{task.title}</li>
))}
</ul>
)}
<ModalAdd
isOpen={isOpen}
setIsOpen={setIsOpen}
saveTask={saveTask}
/>
<IonFab>
<IonFabButton color={"success"} onClick={() => setIsOpen(true)}>
<IonIcon icon={add}></IonIcon>
</IonFabButton>
</IonFab>
</div>
);
};
export default ExploreContainer;
export default ExploreContainer;

View File

@ -0,0 +1,6 @@
.color-green {
color: rgb(41, 228, 41);
}

View File

@ -8,16 +8,16 @@ const Home: React.FC = () => {
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Blank</IonTitle>
<IonTitle><strong>Moxitech To<b className='color-green'>Do</b></strong></IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Blank</IonTitle>
<IonTitle size="large"><strong>Moxitech TO<b className='color-green'>DO</b></strong></IonTitle>
</IonToolbar>
</IonHeader>
<ExploreContainer />
<ExploreContainer/>
</IonContent>
</IonPage>
);

68
src/service/TaskLoader.ts Normal file
View File

@ -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<Task[]>([]);
const [loading, setLoading] = useState<boolean>(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 };
};