diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9caa660 --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +# Database configuration + +PG_HOST: +PG_USERNAME: +PG_PASSWORD: +RAILS_MAX_THREADS: 5 +PG_PORT: 5432 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6380595..5110ff6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ /.bundle # Ignore all environment files. -/.env* +/.env # Ignore all logfiles and tempfiles. /log/* diff --git a/Gemfile b/Gemfile index 403873b..1e4cebe 100644 --- a/Gemfile +++ b/Gemfile @@ -76,3 +76,5 @@ gem "dotenv-rails", "~> 3.1", groups: [ :development, :test ] gem "inertia_rails", "~> 3.11" gem "vite_rails", "~> 3.0" + +gem "devise", "~> 4.9" diff --git a/Gemfile.lock b/Gemfile.lock index b669f9f..1cc1ff0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -79,6 +79,7 @@ GEM public_suffix (>= 2.0.2, < 7.0) ast (2.4.3) base64 (0.3.0) + bcrypt (3.1.20) bcrypt_pbkdf (1.1.1) bigdecimal (3.3.1) bindex (0.8.1) @@ -110,6 +111,12 @@ GEM debug (1.11.0) irb (~> 1.10) reline (>= 0.3.8) + devise (4.9.4) + bcrypt (~> 3.0) + orm_adapter (~> 0.1) + railties (>= 4.1.0) + responders + warden (~> 1.2.3) diff-lcs (1.6.2) dotenv (3.1.8) dotenv-rails (3.1.8) @@ -216,6 +223,7 @@ GEM racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-musl) racc (~> 1.4) + orm_adapter (0.5.0) ostruct (0.6.3) parallel (1.27.0) parser (3.3.10.0) @@ -291,6 +299,9 @@ GEM regexp_parser (2.11.3) reline (0.6.2) io-console (~> 0.5) + responders (3.2.0) + actionpack (>= 7.0) + railties (>= 7.0) rexml (3.4.4) rspec-core (3.13.6) rspec-support (~> 3.13.0) @@ -400,6 +411,8 @@ GEM mutex_m rack-proxy (~> 0.6, >= 0.6.1) zeitwerk (~> 2.2) + warden (1.2.9) + rack (>= 2.0.9) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -431,6 +444,7 @@ DEPENDENCIES capybara database_cleaner-active_record debug + devise (~> 4.9) dotenv-rails (~> 3.1) factory_bot_rails image_processing (~> 1.2) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index fe93333..7fa6be9 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -8,3 +8,9 @@ * * Consider organizing styles into separate files for maintainability. */ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c353756..7498491 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,13 @@ class ApplicationController < ActionController::Base - # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. - allow_browser versions: :modern + before_action :authenticate_user! - # Changes to the importmap will invalidate the etag for HTML responses - stale_when_importmap_changes + inertia_share do + { + flash: { + notice: flash.notice, + alert: flash.alert + }, + current_user: current_user&.slice(:id, :email) + } + end end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..832aa09 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,7 @@ +class HomeController < ApplicationController + def index + render inertia: "Home", props: { + welcomeMessage: "Welcome to the Home Page!" + } + end +end diff --git a/app/controllers/inertia_example_controller.rb b/app/controllers/inertia_example_controller.rb deleted file mode 100644 index bcc4f2e..0000000 --- a/app/controllers/inertia_example_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class InertiaExampleController < ApplicationController - def index - render inertia: "InertiaExample", props: { - name: params.fetch(:name, "World") - } - end -end diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb new file mode 100644 index 0000000..11f89ae --- /dev/null +++ b/app/controllers/users/registrations_controller.rb @@ -0,0 +1,5 @@ +class Users::RegistrationsController < Devise::RegistrationsController + def new + render inertia: "Auth/Register" + end +end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb new file mode 100644 index 0000000..9d658c3 --- /dev/null +++ b/app/controllers/users/sessions_controller.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class Users::SessionsController < Devise::SessionsController + # before_action :configure_sign_in_params, only: [:create] + + # GET /resource/sign_in + def new + render inertia: "Auth/Login" + end + + # POST /resource/sign_in + # def create + # super + # end + + # DELETE /resource/sign_out + # def destroy + # super + # end + + # protected + + # If you have extra params to permit, append them to the sanitizer. + # def configure_sign_in_params + # devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute]) + # end +end diff --git a/app/frontend/entrypoints/application.css b/app/frontend/entrypoints/application.css new file mode 100644 index 0000000..5b6fa40 --- /dev/null +++ b/app/frontend/entrypoints/application.css @@ -0,0 +1,14 @@ +/* Frontend Application Styles */ +@import "tailwindcss"; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + line-height: 1.6; +} + diff --git a/app/frontend/entrypoints/inertia.js b/app/frontend/entrypoints/inertia.js index ef1e1b6..98ba350 100644 --- a/app/frontend/entrypoints/inertia.js +++ b/app/frontend/entrypoints/inertia.js @@ -1,4 +1,5 @@ // frontend/entrypoints/inertia.js +import './application.css' import { createInertiaApp } from '@inertiajs/react' import { createElement } from 'react' import { createRoot } from 'react-dom/client' @@ -7,10 +8,14 @@ import Layout from '../components/Layout' createInertiaApp({ resolve: (name) => { const pages = import.meta.glob('../pages/**/*.jsx', { eager: true }) - let page = pages[`../pages/${name}.jsx`] + const page = pages[`../pages/${name}.jsx`] + + if (!page) { + throw new Error(`Page not found: ${name}`) + } // Se a página NÃO tiver um layout próprio, aplica o Layout padrão - page.default.layout = page.default.layout || (() => createElement(Layout, { children: page })) + page.default.layout = page.default.layout || ((page) => createElement(Layout, null, page)) return page }, diff --git a/app/frontend/pages/Auth/Login.jsx b/app/frontend/pages/Auth/Login.jsx new file mode 100644 index 0000000..e40c59d --- /dev/null +++ b/app/frontend/pages/Auth/Login.jsx @@ -0,0 +1,111 @@ +import { useForm, Link } from '@inertiajs/react' + +export default function Login() { + const { data, setData, post, processing, errors } = useForm({ + user: { email: '', + password: '', + remember_me: false} + }) + + const handleSubmit = (e) => { + e.preventDefault() + // POST para a rota do Devise + post('/users/sign_in', { + data: {user: {data}}, + onError: (errors) => { + console.log('Erro no login:', errors) + } + }) + } + + return ( +
+
+ +
+
+

Login

+ +
+ {/* Campo de Email */} +
+ + setData('user.email', e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500" + autoFocus + /> + {errors.user?.email && ( +
{errors.user.email}
+ )} +
+ + {/* Campo de Senha */} +
+ + setData('user.password', e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500" + /> + {errors.user?.password && ( +
{errors.user.password}
+ )} +
+ + {/* Checkbox Lembrar-me */} +
+ setData('user.remember_me', e.target.checked)} + className="mr-2" + /> + +
+ + {/* Erro geral (se houver) */} + {errors.error && ( +
+ {errors.error} +
+ )} + + {/* Botão de Submit */} + + + {/* Links auxiliares */} +
+ + Não tem conta? Cadastre-se + +
+ +
+ + Esqueceu a senha? + +
+
+
+
+ ) +} diff --git a/app/frontend/pages/Auth/Register.jsx b/app/frontend/pages/Auth/Register.jsx new file mode 100644 index 0000000..ef015b2 --- /dev/null +++ b/app/frontend/pages/Auth/Register.jsx @@ -0,0 +1,105 @@ +import { useForm, Link } from '@inertiajs/react' + +export default function Register() { + const { data, setData, post, processing, errors } = useForm({ + email: '', + password: '', + password_confirmation: '' + }) + + const handleSubmit = (e) => { + e.preventDefault() + // POST para a rota do Devise + post('/users', { + data: { user: data }, + onError: (errors) => { + console.log('Erro no cadastro:', errors) + } + }) + } + + return ( +
+
+

Criar Conta

+ +
+ {/* Campo de Email */} +
+ + setData('email', e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500" + autoFocus + /> + {errors.email && ( +
{errors.email}
+ )} +
+ + {/* Campo de Senha */} +
+ + setData('password', e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500" + /> + {errors.password && ( +
{errors.password}
+ )} +
+ + {/* Campo de Confirmação de Senha */} +
+ + setData('password_confirmation', e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500" + /> + {errors.password_confirmation && ( +
{errors.password_confirmation}
+ )} +
+ + {/* Erro geral (se houver) */} + {errors.error && ( +
+ {errors.error} +
+ )} + + {/* Botão de Submit */} + + + {/* Link para login */} +
+ + Já tem conta? Faça login + +
+
+
+
+ ) +} diff --git a/app/frontend/pages/Home.jsx b/app/frontend/pages/Home.jsx new file mode 100644 index 0000000..2434164 --- /dev/null +++ b/app/frontend/pages/Home.jsx @@ -0,0 +1,15 @@ +import { router } from '@inertiajs/react' + +const Home = () => { + return ( +
+

Olá, tudo bem?

+

Bem-vindo ao Tasklamation!

+ +
+ ) +} + +export default Home \ No newline at end of file diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb new file mode 100644 index 0000000..23de56a --- /dev/null +++ b/app/helpers/home_helper.rb @@ -0,0 +1,2 @@ +module HomeHelper +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..4756799 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,6 @@ +class User < ApplicationRecord + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :validatable +end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 8820690..106110b 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,10 +1,10 @@ - <%= content_for(:title) || "Organiza Feature" %> + Tasklamation - + <%= csrf_meta_tags %> <%= csp_meta_tag %> @@ -20,13 +20,11 @@ <%# Includes all stylesheet files in app/assets/stylesheets %> <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %> - <%= javascript_importmap_tags %> <%= vite_stylesheet_tag "application" %> <%= vite_react_refresh_tag %> <%= vite_client_tag %> <%= vite_javascript_tag "inertia" %> <%= inertia_ssr_head %> - <%= vite_javascript_tag 'application' %>