Как стать автором
Обновить

Приложение на React c нуля до деплоя с помощью Cursor без строчки кода

Уровень сложностиСредний
Время на прочтение14 мин
Количество просмотров12K

Самое популярное приложение после Hello World на react - это личный планировщик задач Todolist и мы не будем сильно оригинальничать и напишем его с нуля на react. Разместим приложение в docker контейнере и поможет нам в этом Cursor AI IDE, а точнее сделает все за нас.

Разрабатывать приложение будем в ОС Windows 10, упакуем в docker контейнер и после разместим на хостинге.

Скачаем и установим Cursor IDE.

Установим Node.js на Windows, для этого скачаем и установим Node.js LTS, проверим установку командой:

node -v

Если установка прошла успешно, в терминале отобразится установленная версия Node.js, теперь инициализируем новый проект:

npm create vite@latest todolist -- --template react

При получении ошибки UnauthorizedAccess в терминале:

Скрытый текст
  1. Откройте PowerShell от имени администратора

  2. Проверьте Текущую Политику Выполнения

    Get-ExecutionPolicy

    Если он показывает "Ограничено", вам нужно его изменить.

  3. Установите Политику выполнения на Неограниченную

    Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

  4. Проверьте изменение: запустите этот cmd

    enter code here

  5. Повторите попытку выполнения команды

    npm create vite@latest todolist -- --template react

Запустим приложение npm startв результате получим следующий вывод:

You can now view todolist in the browser.

Local: http://localhost:3000

Note that the development build is not optimized.
To create a production build, use npm run build.

Мы запустили простейшее приложение на react, а значит подготовили рабочее окружение для разработки собственного приложения на react.

Начнем и озадачим чат, написав ему следующее:

После попытки запустить приложение я получил кучу ошибок

Скрытый текст
ERROR in src/App.tsx:1:33
TS7016: Could not find a declaration file for module 'react'. 'C:/Разработка/todolist/node_modules/react/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/react` if it exists or add a new declaration (.d.ts) file containing `declare module 'react';`
  > 1 | import React, { useState } from 'react';
      |                                 ^^^^^^^
    2 | import './App.css';
    3 |
    4 | interface Todo {
ERROR in src/App.tsx:31:24
TS7006: Parameter 'todo' implicitly has an 'any' type.
    29 |
    30 |   const handleToggleTodo = (id: number) => {
  > 31 |     setTodos(todos.map(todo =>
       |                        ^^^^
    32 |       todo.id === id ? { ...todo, completed: !todo.completed } : todo
    33 |     ));
    34 |   };
ERROR in src/App.tsx:37:27
TS7006: Parameter 'todo' implicitly has an 'any' type.
    35 |
    36 |   const handleDeleteTodo = (id: number) => {
  > 37 |     setTodos(todos.filter(todo => todo.id !== id));
       |                           ^^^^
    38 |   };
    39 |
    40 |   const filteredTodos = todos.filter(todo => {
ERROR in src/App.tsx:40:38
TS7006: Parameter 'todo' implicitly has an 'any' type.
    38 |   };
    39 |
  > 40 |   const filteredTodos = todos.filter(todo => {
       |                                      ^^^^
    41 |     if (filter === 'active') return !todo.completed;
    42 |     if (filter === 'completed') return todo.completed;
    43 |     return true;
ERROR in src/App.tsx:46:39
TS7006: Parameter 'todo' implicitly has an 'any' type.
    44 |   });
    45 |
  > 46 |   const remainingTasks = todos.filter(todo => !todo.completed).length;
       |                                       ^^^^
    47 |
    48 |   return (
    49 |     <div className="App">
ERROR in src/App.tsx:49:5
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    47 |
    48 |   return (
  > 49 |     <div className="App">
       |     ^^^^^^^^^^^^^^^^^^^^^
    50 |       <div className="container">
    51 |         <h1>Todo List</h1>
    52 |         <form onSubmit={handleAddTodo} className="todo-form">
ERROR in src/App.tsx:49:5
TS7016: Could not find a declaration file for module 'react/jsx-runtime'. 'C:/Разработка/todolist/node_modules/react/jsx-runtime.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/react` if it exists or add a new declaration (.d.ts) file containing `declare module 'react/jsx-runtime';`
     47 |
     48 |   return (
  >  49 |     <div className="App">
        |     ^^^^^^^^^^^^^^^^^^^^^
  >  50 |       <div className="container">
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  51 |         <h1>Todo List</h1>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  52 |         <form onSubmit={handleAddTodo} className="todo-form">
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  53 |           <input
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  54 |             type="text"
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  55 |             value={inputValue}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  56 |             onChange={(e) => setInputValue(e.target.value)}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  57 |             placeholder="Add a new task..."
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  58 |             className="todo-input"
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  59 |           />
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  60 |           <button type="submit" className="add-button">Add</button>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  61 |         </form>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  62 |         
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  63 |         <div className="filters">
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  64 |           <button
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  65 |             className={`filter-button ${filter === 'all' ? 'active' : ''}`}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  66 |             onClick={() => setFilter('all')}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  67 |           >
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  68 |             All
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  69 |           </button>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  70 |           <button
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  71 |             className={`filter-button ${filter === 'active' ? 'active' : ''}`}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  72 |             onClick={() => setFilter('active')}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  73 |           >
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  74 |             Active
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  75 |           </button>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  76 |           <button
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  77 |             className={`filter-button ${filter === 'completed' ? 'active' : ''}`}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  78 |             onClick={() => setFilter('completed')}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  79 |           >
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  80 |             Completed
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  81 |           </button>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  82 |         </div>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  83 |
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  84 |         <div className="todo-stats">
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  85 |           {remainingTasks} {remainingTasks === 1 ? 'task' : 'tasks'} remaining
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  86 |         </div>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  87 |
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  88 |         <ul className="todo-list">
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  89 |           {filteredTodos.map(todo => (
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  90 |             <li key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  91 |               <span
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  92 |                 onClick={() => handleToggleTodo(todo.id)}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  93 |                 className="todo-text"
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  94 |               >
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  95 |                 {todo.text}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  96 |               </span>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  97 |               <button
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  98 |                 onClick={() => handleDeleteTodo(todo.id)}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  99 |                 className="delete-button"
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 100 |               >
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 101 |                 Delete
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 102 |               </button>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 103 |             </li>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 104 |           ))}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 105 |         </ul>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 106 |       </div>
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 107 |     </div>
        | ^^^^^^^^^^^
    108 |   );
    109 | }
    110 |
ERROR in src/App.tsx:50:7
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    48 |   return (
    49 |     <div className="App">
  > 50 |       <div className="container">
       |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    51 |         <h1>Todo List</h1>
    52 |         <form onSubmit={handleAddTodo} className="todo-form">
    53 |           <input
ERROR in src/App.tsx:51:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    49 |     <div className="App">
    50 |       <div className="container">
  > 51 |         <h1>Todo List</h1>
       |         ^^^^
    52 |         <form onSubmit={handleAddTodo} className="todo-form">
    53 |           <input
    54 |             type="text"
ERROR in src/App.tsx:51:22
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    49 |     <div className="App">
    50 |       <div className="container">
  > 51 |         <h1>Todo List</h1>
       |                      ^^^^^
    52 |         <form onSubmit={handleAddTodo} className="todo-form">
    53 |           <input
    54 |             type="text"
ERROR in src/App.tsx:52:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    50 |       <div className="container">
    51 |         <h1>Todo List</h1>
  > 52 |         <form onSubmit={handleAddTodo} className="todo-form">
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    53 |           <input
    54 |             type="text"
    55 |             value={inputValue}
ERROR in src/App.tsx:53:11
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    51 |         <h1>Todo List</h1>
    52 |         <form onSubmit={handleAddTodo} className="todo-form">
  > 53 |           <input
       |           ^^^^^^
  > 54 |             type="text"
       | ^^^^^^^^^^^^^^^^^^^^^^^
  > 55 |             value={inputValue}
       | ^^^^^^^^^^^^^^^^^^^^^^^
  > 56 |             onChange={(e) => setInputValue(e.target.value)}
       | ^^^^^^^^^^^^^^^^^^^^^^^
  > 57 |             placeholder="Add a new task..."
       | ^^^^^^^^^^^^^^^^^^^^^^^
  > 58 |             className="todo-input"
       | ^^^^^^^^^^^^^^^^^^^^^^^
  > 59 |           />
       | ^^^^^^^^^^^^^
    60 |           <button type="submit" className="add-button">Add</button>
    61 |         </form>
    62 |         
ERROR in src/App.tsx:56:24
TS7006: Parameter 'e' implicitly has an 'any' type.
    54 |             type="text"
    55 |             value={inputValue}
  > 56 |             onChange={(e) => setInputValue(e.target.value)}
       |                        ^
    57 |             placeholder="Add a new task..."
    58 |             className="todo-input"
    59 |           />
ERROR in src/App.tsx:60:11
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    58 |             className="todo-input"
    59 |           />
  > 60 |           <button type="submit" className="add-button">Add</button>
       |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    61 |         </form>
    62 |         
    63 |         <div className="filters">
ERROR in src/App.tsx:60:59
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    58 |             className="todo-input"
    59 |           />
  > 60 |           <button type="submit" className="add-button">Add</button>
       |                                                           ^^^^^^^^^
    61 |         </form>
    62 |         
    63 |         <div className="filters">
ERROR in src/App.tsx:61:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    59 |           />
    60 |           <button type="submit" className="add-button">Add</button>
  > 61 |         </form>
       |         ^^^^^^^
    62 |         
    63 |         <div className="filters">
    64 |           <button
ERROR in src/App.tsx:63:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    61 |         </form>
    62 |         
  > 63 |         <div className="filters">
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^
    64 |           <button
    65 |             className={`filter-button ${filter === 'all' ? 'active' : ''}`}
    66 |             onClick={() => setFilter('all')}
ERROR in src/App.tsx:64:11
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    62 |         
    63 |         <div className="filters">
  > 64 |           <button
       |           ^^^^^^^
  > 65 |             className={`filter-button ${filter === 'all' ? 'active' : ''}`}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 66 |             onClick={() => setFilter('all')}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 67 |           >
       | ^^^^^^^^^^^^
    68 |             All
    69 |           </button>
    70 |           <button
ERROR in src/App.tsx:69:11
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    67 |           >
    68 |             All
  > 69 |           </button>
       |           ^^^^^^^^^
    70 |           <button
    71 |             className={`filter-button ${filter === 'active' ? 'active' : ''}`}
    72 |             onClick={() => setFilter('active')}
ERROR in src/App.tsx:70:11
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    68 |             All
    69 |           </button>
  > 70 |           <button
       |           ^^^^^^^
  > 71 |             className={`filter-button ${filter === 'active' ? 'active' : ''}`}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 72 |             onClick={() => setFilter('active')}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 73 |           >
       | ^^^^^^^^^^^^
    74 |             Active
    75 |           </button>
    76 |           <button
ERROR in src/App.tsx:75:11
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    73 |           >
    74 |             Active
  > 75 |           </button>
       |           ^^^^^^^^^
    76 |           <button
    77 |             className={`filter-button ${filter === 'completed' ? 'active' : ''}`}
    78 |             onClick={() => setFilter('completed')}
ERROR in src/App.tsx:76:11
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    74 |             Active
    75 |           </button>
  > 76 |           <button
       |           ^^^^^^^
  > 77 |             className={`filter-button ${filter === 'completed' ? 'active' : ''}`}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 78 |             onClick={() => setFilter('completed')}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 79 |           >
       | ^^^^^^^^^^^^
    80 |             Completed
    81 |           </button>
    82 |         </div>
ERROR in src/App.tsx:81:11
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    79 |           >
    80 |             Completed
  > 81 |           </button>
       |           ^^^^^^^^^
    82 |         </div>
    83 |
    84 |         <div className="todo-stats">
ERROR in src/App.tsx:82:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    80 |             Completed
    81 |           </button>
  > 82 |         </div>
       |         ^^^^^^
    83 |
    84 |         <div className="todo-stats">
    85 |           {remainingTasks} {remainingTasks === 1 ? 'task' : 'tasks'} remaining
ERROR in src/App.tsx:84:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    82 |         </div>
    83 |
  > 84 |         <div className="todo-stats">
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    85 |           {remainingTasks} {remainingTasks === 1 ? 'task' : 'tasks'} remaining
    86 |         </div>
    87 |
ERROR in src/App.tsx:86:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    84 |         <div className="todo-stats">
    85 |           {remainingTasks} {remainingTasks === 1 ? 'task' : 'tasks'} remaining
  > 86 |         </div>
       |         ^^^^^^
    87 |
    88 |         <ul className="todo-list">
    89 |           {filteredTodos.map(todo => (
ERROR in src/App.tsx:88:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    86 |         </div>
    87 |
  > 88 |         <ul className="todo-list">
       |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
    89 |           {filteredTodos.map(todo => (
    90 |             <li key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}>
    91 |               <span
ERROR in src/App.tsx:89:30
TS7006: Parameter 'todo' implicitly has an 'any' type.
    87 |
    88 |         <ul className="todo-list">
  > 89 |           {filteredTodos.map(todo => (
       |                              ^^^^
    90 |             <li key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}>
    91 |               <span
    92 |                 onClick={() => handleToggleTodo(todo.id)}
ERROR in src/App.tsx:90:13
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    88 |         <ul className="todo-list">
    89 |           {filteredTodos.map(todo => (
  > 90 |             <li key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}>
       |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    91 |               <span
    92 |                 onClick={() => handleToggleTodo(todo.id)}
    93 |                 className="todo-text"
ERROR in src/App.tsx:91:15
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    89 |           {filteredTodos.map(todo => (
    90 |             <li key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}>
  > 91 |               <span
       |               ^^^^^
  > 92 |                 onClick={() => handleToggleTodo(todo.id)}
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 93 |                 className="todo-text"
       | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 94 |               >
       | ^^^^^^^^^^^^^^^^
    95 |                 {todo.text}
    96 |               </span>
    97 |               <button
ERROR in src/App.tsx:96:15
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    94 |               >
    95 |                 {todo.text}
  > 96 |               </span>
       |               ^^^^^^^
    97 |               <button
    98 |                 onClick={() => handleDeleteTodo(todo.id)}
    99 |                 className="delete-button"
ERROR in src/App.tsx:97:15
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
     95 |                 {todo.text}
     96 |               </span>
  >  97 |               <button
        |               ^^^^^^^
  >  98 |                 onClick={() => handleDeleteTodo(todo.id)}
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  >  99 |                 className="delete-button"
        | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 100 |               >
        | ^^^^^^^^^^^^^^^^
    101 |                 Delete
    102 |               </button>
    103 |             </li>
ERROR in src/App.tsx:102:15
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    100 |               >
    101 |                 Delete
  > 102 |               </button>
        |               ^^^^^^^^^
    103 |             </li>
    104 |           ))}
    105 |         </ul>
ERROR in src/App.tsx:103:13
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    101 |                 Delete
    102 |               </button>
  > 103 |             </li>
        |             ^^^^^
    104 |           ))}
    105 |         </ul>
    106 |       </div>
ERROR in src/App.tsx:105:9
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    103 |             </li>
    104 |           ))}
  > 105 |         </ul>
        |         ^^^^^
    106 |       </div>
    107 |     </div>
    108 |   );
ERROR in src/App.tsx:106:7
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    104 |           ))}
    105 |         </ul>
  > 106 |       </div>
        |       ^^^^^^
    107 |     </div>
    108 |   );
    109 | }
ERROR in src/App.tsx:107:5
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
    105 |         </ul>
    106 |       </div>
  > 107 |     </div>
        |     ^^^^^^
    108 |   );
    109 | }
    110 |
ERROR in src/index.tsx:1:19
TS7016: Could not find a declaration file for module 'react'. 'C:/Разработка/todolist/node_modules/react/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/react` if it exists or add a new declaration (.d.ts) file containing `declare module 'react';`
  > 1 | import React from 'react';
      |                   ^^^^^^^
    2 | import ReactDOM from 'react-dom/client';
    3 | import './index.css';
    4 | import App from './App';
ERROR in src/index.tsx:2:22
TS7016: Could not find a declaration file for module 'react-dom/client'. 'C:/Разработка/todolist/node_modules/react-dom/client.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/react-dom` if it exists or add a new declaration (.d.ts) file containing `declare module 'react-dom/client';`
    1 | import React from 'react';
  > 2 | import ReactDOM from 'react-dom/client';
      |                      ^^^^^^^^^^^^^^^^^^
    3 | import './index.css';
    4 | import App from './App';
    5 |
ERROR in src/index.tsx:11:3
TS7016: Could not find a declaration file for module 'react/jsx-runtime'. 'C:/Разработка/todolist/node_modules/react/jsx-runtime.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/react` if it exists or add a new declaration (.d.ts) file containing `declare module 'react/jsx-runtime';`
     9 |
    10 | root.render(
  > 11 |   <React.StrictMode>
       |   ^^^^^^^^^^^^^^^^^^
  > 12 |     <App />
       | ^^^^^^^^^^^
  > 13 |   </React.StrictMode>
       | ^^^^^^^^^^^^^^^^^^^^^^
    14 | ); 

Не придумал ничего лучше, как скормить их все в чат Cursor

и не поверите он все тут же поправил, после запуска открылось приложение:

и отчитался, что сделал

На этом у нас есть готовое приложение, которое можно улучшать и дорабатывать, давая команды ИИ, но приложение работает локально. Останавливаем терминал и оно отключилось.

Теперь наша задача перенести его в интернет. Для того, чтобы запустить приложение мы разворачивали окружение, которое нужно переносить на другой сервер в интернете, чтобы упростить задачу, будем использовать docker на продакт сервере.

Попросим чат сформировать dockerfile для создания докер образа, содержащий все необходимые команды для развертки аналогичного окружения в один клик.

Скрытый текст
# Build stage
FROM node:18-alpine as build

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Build the application
RUN npm run build

# Production stage
FROM nginx:alpine

# Copy nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Copy built files from build stage
COPY --from=build /app/build /usr/share/nginx/html

# Expose port 80
EXPOSE 80

# Start nginx
CMD ["nginx", "-g", "daemon off;"] 

ИИ подготовил dockerfile и конфиг nginx.conf, осталось загрузить весь проект на GitHub, зарегистрировать хостинг, сбилдить образ и запустить контейнер с приложением. Звучит сложно, на самом деле все просто.

Не буду заострять внимание, как отправить проект в github, на самом деле много информации в интернете и все сводится к установке git локально и подключении удаленного репозитория в Cursor

Запушим и получим код проекта в репозитории Github.

Далее разворачиваем сервер, для пробы используем сервер за 1 рубль в dockerhosting.ru, на созданном инстансе уже установлен docker и portainer.

Выполним сборку образа и запуск контейнера. Сборку образа сделаем сделаем в portainer

В portainer перейдем в раздел images и кликнем Build a new image

Укажем наименование наименование todolist и путь репозиторию Github.

Кликаем Build the image и ждем, после проверяем, что новый образ появился

Запуск контейнера выполним, так как нам подсказал наш помощник, войдем на сервер по ssh, выполним команду:

docker run -p 80:80 todolist

Контейнер запущен:

Проверим в portainer, что наш контейнер работает:

Проверим наше приложение по IP адресу сервера в браузере:

На выходе получил готовое приложение ни разу не работая с react. Если интересно, могу попробовать продолжить "разработку приложения", разработав бэк и подключить его к базе данных.

Если будут вопросы, мой телеграм.

Теги:
Хабы:
0
Комментарии23

Публикации

Работа

Ближайшие события