feat: add theme switching and toast notifications
- Implement theme switching functionality with a new ThemeController - Add ToastController for displaying notifications - Update various views for improved layout and styling - Introduce animations for toast notifications These changes enhance the user experience by allowing users to switch between light and dark themes and receive feedback through toast notifications. The UI has been improved for better accessibility and aesthetics.
This commit is contained in:
parent
42d8d5ce1d
commit
87e0c2eec6
@ -18,7 +18,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
@apply text-[3em] tracking-[-2px] mb-[30px] text-center;
|
@apply text-[3em] tracking-[-2px] mb-8 mt-8 text-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
|
@ -8,5 +8,6 @@ import "@hotwired/turbo-rails"
|
|||||||
|
|
||||||
import "flowbite/dist/flowbite.turbo"
|
import "flowbite/dist/flowbite.turbo"
|
||||||
|
|
||||||
import "./controllers"
|
// import "./theme_switch"
|
||||||
|
|
||||||
|
import "./controllers"
|
||||||
|
@ -4,3 +4,10 @@
|
|||||||
// eagerLoadControllersFrom("controllers", application)
|
// eagerLoadControllersFrom("controllers", application)
|
||||||
|
|
||||||
import { application } from "./application"
|
import { application } from "./application"
|
||||||
|
import ThemeController from "./theme_controller"
|
||||||
|
import ToastController from "./toast_controller"
|
||||||
|
|
||||||
|
import HelloController from "./hello_controller"
|
||||||
|
application.register("hello", HelloController)
|
||||||
|
application.register("theme", ThemeController)
|
||||||
|
application.register("toast", ToastController)
|
||||||
|
24
app/javascript/controllers/theme_controller.js
Normal file
24
app/javascript/controllers/theme_controller.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
// Connects to data-controller="theme"
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["toggle"]
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this.initTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
initTheme() {
|
||||||
|
const savedTheme = localStorage.getItem('theme')
|
||||||
|
if(savedTheme) {
|
||||||
|
document.documentElement.setAttribute('data-theme', savedTheme)
|
||||||
|
this.toggleTarget.checked = savedTheme === 'dark'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle(event) {
|
||||||
|
const newTheme = event.target.checked ? 'dark' : 'light'
|
||||||
|
document.documentElement.setAttribute('data-theme', newTheme)
|
||||||
|
localStorage.setItem('theme', newTheme)
|
||||||
|
}
|
||||||
|
}
|
14
app/javascript/controllers/toast_controller.js
Normal file
14
app/javascript/controllers/toast_controller.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
// Connects to data-controller="toast"
|
||||||
|
export default class extends Controller {
|
||||||
|
connect() {
|
||||||
|
// 3秒后自动隐藏
|
||||||
|
setTimeout(() => {
|
||||||
|
this.element.classList.add('animate-fade-out')
|
||||||
|
setTimeout(() => {
|
||||||
|
this.element.remove()
|
||||||
|
}, 500)
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
}
|
22
app/javascript/theme_switch.js
Normal file
22
app/javascript/theme_switch.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 获取切换按钮
|
||||||
|
const themeToggle = document.querySelector('.theme-controller');
|
||||||
|
|
||||||
|
// 监听变化
|
||||||
|
themeToggle.addEventListener('change', (e) => {
|
||||||
|
// 切换 HTML data-theme 属性
|
||||||
|
if(e.target.checked) {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'light');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存主题设置到 localStorage
|
||||||
|
localStorage.setItem('theme', e.target.checked ? 'dark' : 'light');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 页面加载时检查之前的主题设置
|
||||||
|
const savedTheme = localStorage.getItem('theme');
|
||||||
|
if(savedTheme) {
|
||||||
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
||||||
|
themeToggle.checked = savedTheme === 'dark';
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<footer class="footer footer-center bg-base-200 text-base-content rounded p-10">
|
<footer class="footer footer-center bg-base-200 text-base-content rounded p-10 mt-8">
|
||||||
<nav class="grid grid-flow-col gap-4">
|
<nav class="grid grid-flow-col gap-4">
|
||||||
<%= link_to "About", about_url, class: "link link-hover" %>
|
<%= link_to "About", about_url, class: "link link-hover" %>
|
||||||
<%= link_to "Contact", contact_url, class: "link link-hover" %>
|
<%= link_to "Contact", contact_url, class: "link link-hover" %>
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
<div class="navbar bg-base-100 glass">
|
<!-- 固定在顶部容器 -->
|
||||||
|
<div class="fixed top-0 left-0 right-0 z-50">
|
||||||
|
<!-- 响应式内边距 -->
|
||||||
|
<!--<div class="container mx-auto px-3 sm:px-6 lg:px-1 py-4">-->
|
||||||
|
<!-- 顶部菜单栏 -->
|
||||||
|
<div class="navbar
|
||||||
|
backdrop-filter backdrop-blur-lg bg-opacity-30 border-b border-gray-200 border-transparent
|
||||||
|
shadow-md min-h-0 h-12 px-8 lg:px-12">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<%= link_to "sample app", root_url, id: "logo", class: "btn btn-ghost text-xl" %>
|
<%= link_to "sample app", root_url, id: "logo", class: "btn btn-ghost text-xl" %>
|
||||||
</div>
|
</div>
|
||||||
@ -9,18 +16,20 @@
|
|||||||
<li><%= link_to "Help", help_url %></li>
|
<li><%= link_to "Help", help_url %></li>
|
||||||
<% if logged_in? %>
|
<% if logged_in? %>
|
||||||
<li><%= link_to "Users", users_path %></li>
|
<li><%= link_to "Users", users_path %></li>
|
||||||
<li class="dropdown">
|
<li>
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
<details>
|
||||||
|
<summary>
|
||||||
Account <b class="caret"></b>
|
Account <b class="caret"></b>
|
||||||
</a>
|
</summary>
|
||||||
<ul class="dropdown-menu">
|
<ul class="bg-base-100 rounded-t-none p-2">
|
||||||
<li><%= link_to "Profile", current_user %></li>
|
<li><%= link_to "Profile", current_user %></li>
|
||||||
<li><%= link_to "Settings", edit_user_path(current_user) %></li>
|
<li><%= link_to "Settings", edit_user_path(current_user) %></li>
|
||||||
<li class="divider"></li>
|
<div class="divider"></div>
|
||||||
<li>
|
<li>
|
||||||
<%= link_to "Log out", logout_path, data: { turbo_method: :delete } %>
|
<%= link_to "Log out", logout_path, data: { turbo_method: :delete } %>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</details>
|
||||||
</li>
|
</li>
|
||||||
<% else %>
|
<% else %>
|
||||||
<li><%= link_to "Log in", login_path %></li>
|
<li><%= link_to "Log in", login_path %></li>
|
||||||
@ -67,27 +76,11 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<label class="swap swap-rotate">
|
<%= render "layouts/theme_swap" %>
|
||||||
<!-- this hidden checkbox controls the state -->
|
|
||||||
<input type="checkbox" class="theme-controller" value="light" />
|
|
||||||
|
|
||||||
<!-- sun icon -->
|
|
||||||
<svg
|
|
||||||
class="swap-off h-10 w-10 fill-current"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<!-- moon icon -->
|
|
||||||
<svg
|
|
||||||
class="swap-on h-10 w-10 fill-current"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
|
|
||||||
</svg>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--</div>-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 添加一个占位 div,防止内容被覆盖 -->
|
||||||
|
<div aria-hidden="true" class="border-none h-12"></div>
|
37
app/views/layouts/_message.html.erb
Normal file
37
app/views/layouts/_message.html.erb
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<!-- 放在布局文件的 body 标签末尾 -->
|
||||||
|
<div class="toast toast-end">
|
||||||
|
<% flash.each do |message_type, message| %>
|
||||||
|
<% alert_class = case message_type
|
||||||
|
when "success" then "alert alert-success"
|
||||||
|
when "danger", "error" then "alert alert-error"
|
||||||
|
when "warning" then "alert alert-warning"
|
||||||
|
when "info" then "alert alert-info"
|
||||||
|
else "alert"
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
|
||||||
|
<div data-controller="toast" class="<%= alert_class %> animate-slide-in-right">
|
||||||
|
<div>
|
||||||
|
<% case message_type %>
|
||||||
|
<% when "success" %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<% when "error", "danger" %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<% when "warning" %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
|
</svg>
|
||||||
|
<% else %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<% end %>
|
||||||
|
<span><%= message %></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
29
app/views/layouts/_theme_swap.html.erb
Normal file
29
app/views/layouts/_theme_swap.html.erb
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<div data-controller="theme">
|
||||||
|
<label class="swap swap-rotate">
|
||||||
|
<!-- this hidden checkbox controls the state -->
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="theme-controller"
|
||||||
|
data-theme-target="toggle"
|
||||||
|
data-action="change->theme#toggle"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- sun icon -->
|
||||||
|
<svg
|
||||||
|
class="swap-off h-6 w-6 fill-current"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<!-- moon icon -->
|
||||||
|
<svg
|
||||||
|
class="swap-on h-6 w-6 fill-current"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
|
||||||
|
</svg>
|
||||||
|
</label>
|
||||||
|
</div>
|
@ -12,10 +12,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<%= render 'layouts/header' %>
|
<%= render 'layouts/header' %>
|
||||||
<div class="">
|
<div class="">
|
||||||
<% flash.each do |message_type, message| %>
|
<%= render 'layouts/message' %>
|
||||||
<%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
|
|
||||||
<!-- <div class="alert alert-<%#= message_type %>"><%#= message %></div>-->
|
|
||||||
<% end %>
|
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
<%= render 'layouts/footer' %>
|
<%= render 'layouts/footer' %>
|
||||||
<%#= debug(params) if Rails.env.development? %>
|
<%#= debug(params) if Rails.env.development? %>
|
||||||
|
@ -1,24 +1,45 @@
|
|||||||
<% provide(:title, "Log in") %>
|
<% provide(:title, "Log in") %>
|
||||||
|
|
||||||
<h1>Log in</h1>
|
<div class ="container mx-auto px-4">
|
||||||
<div class ="row">
|
<h1 class="text-3xl font-bold text-center my-8">Log in</h1>
|
||||||
<div class="col-md-6 col-md-offset-3">
|
|
||||||
<%= form_with(url: login_path, scope: :session, local: true) do |f| %>
|
|
||||||
<%= f.label :email %>
|
|
||||||
<%= f.email_field :email, class: 'form-control' %>
|
|
||||||
|
|
||||||
<%= f.label :password %>
|
<div class="max-w-md mx-auto">
|
||||||
<%= link_to "(forgot password)", new_password_reset_path %>
|
<%= form_with(url: login_path, scope: :session, local: true, class: "space-y-4") do |f| %>
|
||||||
<%= f.password_field :password, class: 'form-control' %>
|
<div class="form-control">
|
||||||
|
<%= f.label :email, class: "label" do %>
|
||||||
|
<span class="label-text">Email</span>
|
||||||
|
<% end %>
|
||||||
|
<%= f.email_field :email, class: "input input-bordered w-full" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= f.label :remember_me, class: "checkbox inline" do %>
|
<div class="form-control">
|
||||||
<%= f.check_box :remember_me %>
|
<div class="flex justify-between items-center">
|
||||||
<span>Remember me on this computer</span>
|
<%= f.label :password, class: "label" do %>
|
||||||
|
<span class="label-text">Password</span>
|
||||||
|
<% end %>
|
||||||
|
<%= link_to "(forgot password)", new_password_reset_path,
|
||||||
|
class: "text-sm text-primary hover:text-primary-focus" %>
|
||||||
|
</div>
|
||||||
|
<%= f.password_field :password, class: "input input-bordered w-full" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label cursor-pointer">
|
||||||
|
<%= f.check_box :remember_me, class: "checkbox checkbox-primary" %>
|
||||||
|
<span class="label-text ml-2">Remember me on this computer</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control mt-6">
|
||||||
|
<%= f.submit "Log in", class: "btn btn-primary w-full" %>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= f.submit "Log in", class: "btn btn-primary" %>
|
<div class="text-center mt-6">
|
||||||
<% end %>
|
<p>New user?
|
||||||
|
<%= link_to "Sign up now!", signup_path,
|
||||||
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
|
class: "text-primary hover:text-primary-focus" %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,6 +31,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-control mt-6">
|
<div class="form-control mt-6">
|
||||||
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
|
<%= f.submit yield(:button_text), class: "btn btn-primary mb-6" %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
@ -1,11 +1,24 @@
|
|||||||
<li>
|
<div class="card card-compact bg-base-100 shadow-lg hover:shadow-xl transition-shadow">
|
||||||
<%= gravatar_for user, size: 50 %>
|
<div class="card-body flex-row items-center justify-between">
|
||||||
<%= link_to user.name, user %>
|
<!-- 用户基本信息 -->
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<%= gravatar_for user, size: 50, class: "rounded-full" %>
|
||||||
|
<%= link_to user.name, user,
|
||||||
|
class: "link link-hover text-lg font-medium" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 管理员操作按钮 -->
|
||||||
<% if current_user.admin? && !current_user?(user) %>
|
<% if current_user.admin? && !current_user?(user) %>
|
||||||
| <%= link_to "delete", user,
|
<%= link_to user,
|
||||||
data: {
|
data: {
|
||||||
turbo_method: :delete,
|
turbo_method: :delete,
|
||||||
confirm: "You sure?"
|
turbo_confirm: "Are you sure you want to delete this user?"
|
||||||
} %>
|
},
|
||||||
|
class: "btn btn-ghost btn-sm text-error hover:bg-error hover:text-white" do %>
|
||||||
|
<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>
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,13 +1,34 @@
|
|||||||
<% provide(:title, "Edit user") %>
|
<% provide(:title, "Edit user") %>
|
||||||
<% provide(:button_text, 'Save changes') %>
|
<% provide(:button_text, 'Save changes') %>
|
||||||
<h1>Update your profile</h1>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="container mx-auto px-4 py-8">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<div class="max-w-md mx-auto">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<h1 class="text-2xl font-bold text-center mb-8">Update your profile</h1>
|
||||||
|
|
||||||
|
<!-- 表单区域 -->
|
||||||
|
<div class="card bg-base-100 shadow-lg">
|
||||||
|
<div class="card-body">
|
||||||
<%= render 'form' %>
|
<%= render 'form' %>
|
||||||
<div class="gravatar_edit">
|
|
||||||
<%= gravatar_for @user %>
|
|
||||||
<a href="https://gravatar.com/emails" target="_blank">change</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 头像编辑区 -->
|
||||||
|
<div class="card bg-base-100 shadow-lg mb-8">
|
||||||
|
<div class="card-body items-center text-center">
|
||||||
|
<%= gravatar_for @user, size: 100, class: "rounded-full ring ring-primary ring-offset-2" %>
|
||||||
|
|
||||||
|
<%= link_to "https://gravatar.com/emails",
|
||||||
|
target: "_blank",
|
||||||
|
rel: "noopener noreferrer",
|
||||||
|
class: "btn btn-outline btn-sm gap-2 mt-4" do %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
||||||
|
</svg>
|
||||||
|
Change Avatar
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
@ -1,17 +1,15 @@
|
|||||||
<% provide(:title, 'All users') %>
|
<% provide(:title, 'All users') %>
|
||||||
|
|
||||||
<h1>All users</h1>
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<h1 class="text-3xl font-bold text-center mb-8">All Users</h1>
|
||||||
|
|
||||||
<%= paginate @users %>
|
<!-- 分页 -->
|
||||||
|
<div class="flex justify-center mb-6">
|
||||||
|
<%= paginate @users %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户列表 -->
|
||||||
<ul class="users">
|
<div class="grid gap-4 max-w-3xl mx-auto">
|
||||||
<%= render @users %>
|
<%= render @users %>
|
||||||
<%# auto loop by rails, like after %>
|
</div>
|
||||||
<%# @users.each do |user| %>
|
</div>
|
||||||
<!-- <li>-->
|
|
||||||
<%#= gravatar_for user, size: 50 %>
|
|
||||||
<%#= link_to user.name, user %>
|
|
||||||
<!-- </li>-->
|
|
||||||
<%# end %>
|
|
||||||
</ul>
|
|
@ -1,12 +1,25 @@
|
|||||||
<%provide(:title, @user.name) %>
|
<% provide(:title, @user.name) %>
|
||||||
|
|
||||||
<div class="row">
|
<div class="container mx-auto px-4 py-8">
|
||||||
<aside class="col-md-4">
|
<aside class="max-w-2xl mx-auto">
|
||||||
<section class="user_info">
|
<section class="card bg-base-100 shadow-xl">
|
||||||
<h1>
|
<div class="card-body">
|
||||||
<%= gravatar_for @user %>
|
<!-- 头像和用户名布局 -->
|
||||||
|
<h1 class="flex flex-col sm:flex-row items-center gap-6">
|
||||||
|
<!-- 头像添加装饰效果 -->
|
||||||
|
<div class="relative">
|
||||||
|
<%= gravatar_for @user, size: 120, class: "rounded-full ring-2 ring-primary ring-offset-2 hover:ring-4 transition-all duration-300" %>
|
||||||
|
<div class="absolute inset-0 rounded-full bg-primary/10 animate-pulse"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户名样式 -->
|
||||||
|
<div class="text-center sm:text-left">
|
||||||
|
<span class="text-2xl font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
|
||||||
<%= @user.name %>
|
<%= @user.name %>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</h1>
|
</h1>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
@ -13,6 +13,30 @@ module.exports = {
|
|||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
|
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
|
||||||
},
|
},
|
||||||
|
keyframes: {
|
||||||
|
'slide-in-right': {
|
||||||
|
'0%': {
|
||||||
|
transform: 'translateX(100%)',
|
||||||
|
opacity: '0'
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
transform: 'translateX(0)',
|
||||||
|
opacity: '1'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'fade-out': {
|
||||||
|
'0%': {
|
||||||
|
opacity: '1'
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
opacity: '0'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
'slide-in-right': 'slide-in-right 0.5s ease-out',
|
||||||
|
'fade-out': 'fade-out 0.5s ease-out'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
Loading…
Reference in New Issue
Block a user