Asistent AI

Structură aplicație asistată de AI

Descrie aplicația, iar AI va crea structura JSON pentru tine

Folosește AI pentru generare inteligentă de structură
Creează pagini, tabele și câmpuri
Respectă bune practici pentru modelarea datelor

La zi

{
  "pages": [
    {
      "slug": "todo-list",
      "title": "Todo List",
      "description": "A page to create and view todo items.",
      "components": [
        {
          "component_name": "Loading Bar",
          "component_description": "A thin animated loading bar shown at the top of the page during SDK requests.",
          "component_code": "<div id=\"loading-bar-container\" class=\"fixed top-0 left-0 w-full h-1 z-50 pointer-events-none hidden\">\n  <div id=\"loading-bar\" class=\"h-full bg-blue-600 dark:bg-blue-400 animate-loading-bar\" style=\"width: 0%\"></div>\n</div>\n<style>\n  @keyframes loading-progress {\n    0% { width: 0%; }\n    20% { width: 30%; }\n    40% { width: 55%; }\n    60% { width: 75%; }\n    80% { width: 90%; }\n    100% { width: 100%; }\n  }\n  .animate-loading-bar {\n    animation: loading-progress 1.5s ease-out forwards;\n  }\n  @keyframes loading-fade-out {\n    0% { opacity: 1; }\n    100% { opacity: 0; }\n  }\n  .animate-loading-fade {\n    animation: loading-fade-out 0.3s ease-out forwards;\n  }\n</style>",
          "component_script": "let _loadingCount = 0;\n\nfunction showLoading() {\n  _loadingCount++;\n  const container = document.getElementById('loading-bar-container');\n  const bar = document.getElementById('loading-bar');\n  if (container) {\n    container.classList.remove('hidden');\n    bar.classList.remove('animate-loading-fade');\n    bar.style.width = '0%';\n    bar.offsetHeight;\n    bar.classList.add('animate-loading-bar');\n  }\n}\n\nfunction hideLoading() {\n  _loadingCount = Math.max(0, _loadingCount - 1);\n  if (_loadingCount > 0) return;\n  const container = document.getElementById('loading-bar-container');\n  const bar = document.getElementById('loading-bar');\n  if (container && bar) {\n    bar.style.width = '100%';\n    bar.classList.remove('animate-loading-bar');\n    bar.classList.add('animate-loading-fade');\n    setTimeout(() => {\n      if (_loadingCount === 0) {\n        container.classList.add('hidden');\n        bar.classList.remove('animate-loading-fade');\n        bar.style.width = '0%';\n      }\n    }, 300);\n  }\n}"
        },
        {
          "component_name": "Todo List",
          "component_description": "Displays a list of todo items with styled cards, including mark as done, edit and delete functionality.",
          "component_code": "<div class=\"mt-5 w-full max-w-xl mx-auto\">\n  <h2 class=\"text-2xl font-bold text-gray-900 dark:text-gray-100 mb-4\">My Todos</h2>\n  <div id=\"todo-list\" class=\"space-y-2\"></div>\n  <p id=\"empty-state\" class=\"text-gray-400 dark:text-gray-500 text-center py-8 hidden\">No todos yet. Add one below!</p>\n</div>",
          "component_script": "let _todosCache = [];\n\nfunction isDone(todo) {\n  return todo.done === true || todo.done === 'true' || todo.done === 1;\n}\n\nfunction escapeAttr(str) {\n  return String(str).replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\nasync function fetchTodos() {\n  showLoading();\n  try {\n    _todosCache = await sdk.getRecords('todos');\n    renderTodos();\n  } catch (error) {\n    console.error(error);\n  } finally {\n    hideLoading();\n  }\n}\n\nfunction renderTodos() {\n  const listEl = document.getElementById('todo-list');\n  const emptyEl = document.getElementById('empty-state');\n  listEl.innerHTML = '';\n  if (_todosCache.length === 0) {\n    emptyEl.classList.remove('hidden');\n  } else {\n    emptyEl.classList.add('hidden');\n    _todosCache.forEach(todo => {\n      const done = isDone(todo);\n      const nameClass = done ? 'line-through text-gray-400 dark:text-gray-500' : 'text-gray-900 dark:text-gray-100';\n      const cardClass = done ? 'bg-gray-50 dark:bg-gray-800/50 border-gray-100 dark:border-gray-700/50' : 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700';\n      const checkBtnClass = done\n        ? 'bg-emerald-500 dark:bg-emerald-400 border-emerald-500 dark:border-emerald-400'\n        : 'border-gray-300 dark:border-gray-500 hover:border-emerald-400 dark:hover:border-emerald-400';\n      const checkSvg = done\n        ? '<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-white dark:text-gray-900\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"3\" d=\"M5 13l4 4L19 7\"/></svg>'\n        : '';\n      const safeName = escapeAttr(todo.name);\n      const todoId = todo.id;\n\n      const div = document.createElement('div');\n      div.id = 'todo-item-' + todoId;\n      div.className = 'flex items-center justify-between ' + cardClass + ' border rounded-lg px-4 py-3 shadow-sm hover:shadow-md transition-shadow';\n      div.innerHTML = `\n        <div id=\"todo-view-${todoId}\" class=\"flex items-center gap-3 w-full\">\n          <button id=\"todo-check-${todoId}\" class=\"flex-shrink-0 w-6 h-6 rounded-full border-2 flex items-center justify-center transition-colors ${checkBtnClass}\" title=\"${done ? 'Mark as undone' : 'Mark as done'}\">\n            ${checkSvg}\n          </button>\n          <span class=\"${nameClass} text-base flex-1 transition-colors\">${todo.name}</span>\n          <div class=\"flex items-center gap-1\">\n            <button id=\"todo-edit-btn-${todoId}\" class=\"text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 transition-colors p-1 rounded hover:bg-blue-50 dark:hover:bg-blue-900/30\" title=\"Edit\">\n              <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"/></svg>\n            </button>\n            <button id=\"todo-delete-btn-${todoId}\" class=\"text-rose-500 hover:text-rose-700 dark:text-rose-400 dark:hover:text-rose-300 transition-colors p-1 rounded hover:bg-rose-50 dark:hover:bg-rose-900/30\" title=\"Delete\">\n              <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"/></svg>\n            </button>\n          </div>\n        </div>\n        <div id=\"todo-edit-${todoId}\" class=\"hidden flex items-center gap-2 w-full\">\n          <input id=\"todo-edit-input-${todoId}\" type=\"text\" value=\"${safeName}\" class=\"flex-1 px-3 py-1.5 rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-transparent text-sm\">\n          <button id=\"todo-save-btn-${todoId}\" class=\"px-3 py-1.5 bg-emerald-600 hover:bg-emerald-700 dark:bg-emerald-500 dark:hover:bg-emerald-600 text-white text-sm font-medium rounded-md transition-colors\">Save</button>\n          <button id=\"todo-cancel-btn-${todoId}\" class=\"px-3 py-1.5 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-500 text-gray-700 dark:text-gray-200 text-sm font-medium rounded-md transition-colors\">Cancel</button>\n        </div>\n      `;\n      listEl.appendChild(div);\n\n      document.getElementById('todo-check-' + todoId).addEventListener('click', function() {\n        toggleDone(todoId);\n      });\n      document.getElementById('todo-edit-btn-' + todoId).addEventListener('click', function() {\n        startEdit(todoId, todo.name);\n      });\n      document.getElementById('todo-delete-btn-' + todoId).addEventListener('click', function() {\n        deleteTodo(todoId);\n      });\n      document.getElementById('todo-save-btn-' + todoId).addEventListener('click', function() {\n        saveEdit(todoId);\n      });\n      document.getElementById('todo-cancel-btn-' + todoId).addEventListener('click', function() {\n        cancelEdit(todoId);\n      });\n      document.getElementById('todo-edit-input-' + todoId).addEventListener('keydown', function(e) {\n        handleEditKey(e, todoId);\n      });\n    });\n  }\n}\n\nasync function toggleDone(id) {\n  const todo = _todosCache.find(t => t.id === id);\n  if (!todo) return;\n  const newDone = !isDone(todo);\n  showLoading();\n  try {\n    await sdk.updateRecord('todos', id, { done: newDone });\n    fetchTodos();\n  } catch (error) {\n    console.error(error);\n    hideLoading();\n  }\n}\n\nfunction startEdit(id, currentName) {\n  const viewEl = document.getElementById('todo-view-' + id);\n  const editEl = document.getElementById('todo-edit-' + id);\n  const inputEl = document.getElementById('todo-edit-input-' + id);\n  viewEl.classList.add('hidden');\n  editEl.classList.remove('hidden');\n  inputEl.value = currentName;\n  inputEl.focus();\n  inputEl.select();\n}\n\nfunction cancelEdit(id) {\n  const viewEl = document.getElementById('todo-view-' + id);\n  const editEl = document.getElementById('todo-edit-' + id);\n  viewEl.classList.remove('hidden');\n  editEl.classList.add('hidden');\n}\n\nfunction handleEditKey(event, id) {\n  if (event.key === 'Enter') {\n    saveEdit(id);\n  } else if (event.key === 'Escape') {\n    cancelEdit(id);\n  }\n}\n\nasync function saveEdit(id) {\n  try {\n    const inputEl = document.getElementById('todo-edit-input-' + id);\n    const value = inputEl.value.trim();\n    if (!value) return;\n    showLoading();\n    await sdk.updateRecord('todos', id, { name: value });\n    fetchTodos();\n  } catch (error) {\n    console.error(error);\n    hideLoading();\n  }\n}\n\nasync function deleteTodo(id) {\n  showLoading();\n  try {\n    await sdk.deleteRecord('todos', id);\n    fetchTodos();\n  } catch (error) {\n    console.error(error);\n    hideLoading();\n  }\n}\n\nfetchTodos();\nsdk.on('todo:created', () => fetchTodos());"
        },
        {
          "component_name": "Add Todo",
          "component_description": "A styled form to add new todo items.",
          "component_code": "<div class=\"w-full max-w-xl mx-auto mt-6\">\n  <form id=\"add-todo-form\" class=\"flex gap-3\">\n    <input id=\"todo-input\" type=\"text\" placeholder=\"What needs to be done?\" class=\"flex-1 px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-transparent transition-shadow\">\n    <button type=\"submit\" class=\"px-6 py-3 bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white font-semibold rounded-lg shadow-sm hover:shadow-md transition-all active:scale-95\">Add</button>\n  </form>\n</div>",
          "component_script": "document.getElementById('add-todo-form').addEventListener('submit', async function(event) {\n  event.preventDefault();\n  try {\n    const todoInput = document.getElementById('todo-input');\n    const value = todoInput.value.trim();\n    if (!value) return;\n    showLoading();\n    await sdk.createRecord('todos', { name: value, done: false });\n    todoInput.value = '';\n    sdk.emit('todo:created');\n  } catch (error) {\n    console.error(error);\n  } finally {\n    hideLoading();\n  }\n});"
        }
      ]
    }
  ],
  "tables": [
    {
      "slug": "todos",
      "name": "Todos",
      "description": "A table to store todo items.",
      "fields": [
        {
          "slug": "name",
          "name": "Name",
          "type": "String"
        },
        {
          "slug": "done",
          "name": "Done",
          "type": "Boolean"
        }
      ]
    }
  ]
}