Template
This commit is contained in:
parent
abde6c9bed
commit
187f146766
@ -26,5 +26,9 @@ module.exports = {
|
|||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-debugger': 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-dom": "^18.0.6",
|
||||||
"@types/react-router": "^5.1.11",
|
"@types/react-router": "^5.1.11",
|
||||||
"@types/react-router-dom": "^5.1.7",
|
"@types/react-router-dom": "^5.1.7",
|
||||||
|
"axios": "^1.7.7",
|
||||||
"history": "^4.9.0",
|
"history": "^4.9.0",
|
||||||
"ionicons": "^7.0.0",
|
"ionicons": "^7.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
@ -4845,6 +4846,29 @@
|
|||||||
"node": ">=4"
|
"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": {
|
"node_modules/axobject-query": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
||||||
@ -13058,6 +13082,11 @@
|
|||||||
"node": ">= 0.10"
|
"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": {
|
"node_modules/psl": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/react-router": "^5.1.11",
|
"@types/react-router": "^5.1.11",
|
||||||
"@types/react-router-dom": "^5.1.7",
|
"@types/react-router-dom": "^5.1.7",
|
||||||
|
"axios": "^1.7.7",
|
||||||
"history": "^4.9.0",
|
"history": "^4.9.0",
|
||||||
"ionicons": "^7.0.0",
|
"ionicons": "^7.0.0",
|
||||||
"react": "^18.2.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 {
|
#container a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.color {
|
||||||
|
background-color: #2dd55b;
|
||||||
|
}
|
@ -1,11 +1,41 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import './ExploreContainer.css';
|
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 ExploreContainer: React.FC = () => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const {
|
||||||
|
tasks,
|
||||||
|
loading,
|
||||||
|
saveTask,
|
||||||
|
deleteTask
|
||||||
|
} = useTaskStorage();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="container">
|
<div>
|
||||||
<strong>Ready to create an app?</strong>
|
{loading ? (
|
||||||
<p>Start with Ionic <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.color-green {
|
||||||
|
color: rgb(41, 228, 41);
|
||||||
|
}
|
@ -8,16 +8,16 @@ const Home: React.FC = () => {
|
|||||||
<IonPage>
|
<IonPage>
|
||||||
<IonHeader>
|
<IonHeader>
|
||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonTitle>Blank</IonTitle>
|
<IonTitle><strong>Moxitech To<b className='color-green'>Do</b></strong></IonTitle>
|
||||||
</IonToolbar>
|
</IonToolbar>
|
||||||
</IonHeader>
|
</IonHeader>
|
||||||
<IonContent fullscreen>
|
<IonContent fullscreen>
|
||||||
<IonHeader collapse="condense">
|
<IonHeader collapse="condense">
|
||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonTitle size="large">Blank</IonTitle>
|
<IonTitle size="large"><strong>Moxitech TO<b className='color-green'>DO</b></strong></IonTitle>
|
||||||
</IonToolbar>
|
</IonToolbar>
|
||||||
</IonHeader>
|
</IonHeader>
|
||||||
<ExploreContainer />
|
<ExploreContainer/>
|
||||||
</IonContent>
|
</IonContent>
|
||||||
</IonPage>
|
</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