Template
This commit is contained in:
parent
abde6c9bed
commit
187f146766
@ -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
33
TestServer/cmd/main.go
Normal 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
3
TestServer/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module moxitech/todo/server
|
||||
|
||||
go 1.22.7
|
29
package-lock.json
generated
29
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
56
src/components/ActionButton/ModalAdd.tsx
Normal file
56
src/components/ActionButton/ModalAdd.tsx
Normal 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;
|
@ -22,3 +22,8 @@
|
||||
#container a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
.color {
|
||||
background-color: #2dd55b;
|
||||
}
|
@ -1,11 +1,41 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
|
||||
.color-green {
|
||||
color: rgb(41, 228, 41);
|
||||
}
|
@ -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
68
src/service/TaskLoader.ts
Normal 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 };
|
||||
};
|
Loading…
Reference in New Issue
Block a user