diff --git a/src/components/common/ColorIcon.jsx b/src/components/Layout/ColorIcon.jsx similarity index 100% rename from src/components/common/ColorIcon.jsx rename to src/components/Layout/ColorIcon.jsx diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 56f0ce3..e72814b 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -3,8 +3,7 @@ import { Layout, Switch, Button, Dropdown } from 'antd'; import { UserOutlined, LogoutOutlined } from '@ant-design/icons'; import { useTheme } from '@/contexts/ThemeContext'; import { useAuth } from '@/contexts/AuthContext'; -import { MenuTrigger } from '../common/MenuTrigger'; - +import { MenuTrigger } from '../Layout/MenuTrigger'; const { Header: AntHeader } = Layout; const Header = ({ collapsed, setCollapsed }) => { diff --git a/src/components/Layout/Logo.jsx b/src/components/Layout/Logo.jsx index 6f263bd..510b329 100644 --- a/src/components/Layout/Logo.jsx +++ b/src/components/Layout/Logo.jsx @@ -1,18 +1,17 @@ import React from 'react'; import { RocketOutlined } from '@ant-design/icons'; +import { Typography } from 'antd'; + +const { Text } = Typography; export const Logo = ({ collapsed, isDarkMode }) => (
{!collapsed && ( -

+ Uppeta -

+ )}
diff --git a/src/components/common/MenuTrigger.jsx b/src/components/Layout/MenuTrigger.jsx similarity index 100% rename from src/components/common/MenuTrigger.jsx rename to src/components/Layout/MenuTrigger.jsx diff --git a/src/components/Layout/Sidebar.jsx b/src/components/Layout/Sidebar.jsx index 06d22ef..241c061 100644 --- a/src/components/Layout/Sidebar.jsx +++ b/src/components/Layout/Sidebar.jsx @@ -3,7 +3,7 @@ import { Layout, Menu } from 'antd'; import { useNavigate, useLocation } from 'react-router-dom'; import { useTheme } from '@/contexts/ThemeContext'; import { getMenuItems } from '@/utils/menuUtils'; -import { Logo } from '@/components/common/Logo'; +import { Logo } from '@/components/Layout/Logo'; const { Sider } = Layout; diff --git a/src/components/common/BaseTable.jsx b/src/components/common/BaseTable.jsx deleted file mode 100644 index 9c4a1c6..0000000 --- a/src/components/common/BaseTable.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import { Table } from 'antd'; - -export const BaseTable = ({ - columns, - dataSource, - loading = false, - rowKey = 'id', - ...props -}) => ( - -); \ No newline at end of file diff --git a/src/components/common/Icon.jsx b/src/components/common/Icon.jsx deleted file mode 100644 index 722702e..0000000 --- a/src/components/common/Icon.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -export const Icon = ({ name, className = '', style = {} }) => ( - - {name} - -); \ No newline at end of file diff --git a/src/components/common/Logo.jsx b/src/components/common/Logo.jsx deleted file mode 100644 index 8667cc6..0000000 --- a/src/components/common/Logo.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -export const Logo = ({ collapsed, isDarkMode }) => ( -
-
- - rocket_launch - -

- Uppeta -

-
-
-); diff --git a/src/components/common/PageHeader.jsx b/src/components/common/PageHeader.jsx deleted file mode 100644 index 631787a..0000000 --- a/src/components/common/PageHeader.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { Button } from 'antd'; -import { PlusOutlined } from '@ant-design/icons'; - -export const PageHeader = ({ title, onAdd, addButtonText = '新增' }) => ( -
-

{title}

- {onAdd && ( - - )} -
-); \ No newline at end of file diff --git a/src/config/supabase.js b/src/config/supabase.js index 5f7b4fa..6a073da 100644 --- a/src/config/supabase.js +++ b/src/config/supabase.js @@ -21,7 +21,7 @@ export const createSupabase = () => { detectSessionInUrl: false, }, db: { - schema: 'limq' + schema: 'limq_dev' } } ); diff --git a/src/hooks/supabaseService.js b/src/hooks/supabaseService.js index 8395c97..a2c9fdc 100644 --- a/src/hooks/supabaseService.js +++ b/src/hooks/supabaseService.js @@ -1,7 +1,7 @@ import { supabase } from '@/config/supabase' class SupabaseService { - async get(table, options = {}) { + async select(table, options = {}) { try { let query = supabase .from(table) @@ -96,6 +96,26 @@ class SupabaseService { throw error } } + // 通用 UPSERT 请求 + async upsert(table, data, onConflict) { + try { + let query = supabase + .from(table) + .upsert(data) + .select() + + if (onConflict) { + query = query.onConflict(onConflict) + } + + const { data: result, error } = await query + if (error) throw error + return result + } catch (error) { + console.error(`Error upserting into ${table}:`, error.message) + throw error + } + } } export const supabaseService = new SupabaseService() \ No newline at end of file diff --git a/src/hooks/team/useTeamMemberships.js b/src/hooks/team/useTeamMemberships.js index 4804116..dcd4fea 100644 --- a/src/hooks/team/useTeamMemberships.js +++ b/src/hooks/team/useTeamMemberships.js @@ -10,7 +10,7 @@ export const useTeamMembership = (teamId) => { setLoading(true); try { - const result = await supabaseService.get('team_memberships', { + const result = await SupabaseService.select('team_memberships', { select: '*', relations: { user: 'id, email, name' diff --git a/src/hooks/team/useTeams.js b/src/hooks/team/useTeams.js index b4d8f80..a995438 100644 --- a/src/hooks/team/useTeams.js +++ b/src/hooks/team/useTeams.js @@ -4,7 +4,7 @@ export const useTeams = () => { // 获取团队列表 const fetchTeams = async (params = {}) => { try { - const result = await supabaseService.get('teams', { + const result = await SupabaseService.select('teams', { select: ` id, name, diff --git a/src/pages/auth/Login.jsx b/src/pages/auth/Login.jsx index 1e7a064..4549bcf 100644 --- a/src/pages/auth/Login.jsx +++ b/src/pages/auth/Login.jsx @@ -7,7 +7,7 @@ import { supabaseService } from "@/hooks/supabaseService"; const Login = () => { const navigate = useNavigate(); - const { login, signInWithGoogle, loading } = useAuth(); + const { login, signInWithGoogle, emailLoading, googleLoading } = useAuth(); const [form] = Form.useForm(); const handleLogin = async (values) => { @@ -75,6 +75,8 @@ const Login = () => { type="primary" htmlType="submit" block + loading={emailLoading} + disabled={googleLoading} className="h-12 rounded-xl text-base font-medium bg-gradient-to-r from-blue-600 to-cyan-500 border-0 hover:from-blue-700 hover:to-cyan-600" > 登录 @@ -89,7 +91,8 @@ const Login = () => { icon={} block onClick={signInWithGoogle} - loading={loading} + loading={googleLoading} + disabled={emailLoading} className="h-12 rounded-xl text-base font-medium mb-6 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 border border-gray-200 dark:border-gray-600" > 使用 Google 账号登录 diff --git a/src/pages/company/customer/index.jsx b/src/pages/company/customer/index.jsx index 0691b2c..630c006 100644 --- a/src/pages/company/customer/index.jsx +++ b/src/pages/company/customer/index.jsx @@ -92,7 +92,6 @@ const CustomerPage = () => { dataIndex: 'created_at', key: 'created_at', sorter: true, - width: 180, render: (text) => ( {new Date(text).toLocaleString('zh-CN', { year: 'numeric', @@ -106,7 +105,7 @@ const CustomerPage = () => { { title: '操作', key: 'action', - width: 150, + fixed: 'right', render: (_, record) => (
{ title: "项目名称", dataIndex: "name", key: "name", - width: "25%", render: (text, record) => { const isEditing = record.key === editingKey; return isEditing ? ( @@ -408,7 +407,6 @@ const ServicePage = () => { title: "描述", dataIndex: "description", key: "description", - width: "25%", render: (text, record) => { const isEditing = record.key === editingKey; return isEditing ? ( @@ -454,7 +452,6 @@ const ServicePage = () => { title: "单价", dataIndex: "price", key: "price", - width: "15%", render: (price, record) => { const isEditing = record.key === editingKey; return isEditing ? ( @@ -489,7 +486,6 @@ const ServicePage = () => { title: "数量", dataIndex: "quantity", key: "quantity", - width: "15%", render: (quantity, record) => { const isEditing = record.key === editingKey; return isEditing ? ( @@ -517,7 +513,6 @@ const ServicePage = () => { { title: "小计", key: "subtotal", - width: "20%", render: (_, record) => ( ¥ @@ -534,7 +529,7 @@ const ServicePage = () => { { title: "操作", key: "action", - width: "150px", + fixed: 'right', render: (_, record) => { const isEditing = record.key === editingKey; return isEditing ? ( @@ -630,6 +625,7 @@ const ServicePage = () => { >
({ ...item, key: `${record.id}-${sectionIndex}-${itemIndex}`, diff --git a/src/pages/company/service/sections/index.jsx b/src/pages/company/service/sections/index.jsx index 2b67ea0..70aa509 100644 --- a/src/pages/company/service/sections/index.jsx +++ b/src/pages/company/service/sections/index.jsx @@ -151,28 +151,23 @@ const SectionManagement = () => { { title: '项目名称', dataIndex: 'name', - width: '20%', }, { title: '描述', dataIndex: 'description', - width: '25%', }, { title: '单价', dataIndex: 'price', - width: '15%', render: (price) => `¥${price}` }, { title: '数量', dataIndex: 'quantity', - width: '15%', }, { title: '单位', dataIndex: 'unit', - width: '15%', } ]; @@ -181,7 +176,6 @@ const SectionManagement = () => { { title: '项目名称', dataIndex: 'name', - width: '20%', render: (_, __, index) => ( { { title: '描述', dataIndex: 'description', - width: '25%', render: (_, __, index) => ( { { title: '单价', dataIndex: 'price', - width: '15%', render: (_, __, index) => ( { { title: '数量', dataIndex: 'quantity', - width: '15%', render: (_, __, index) => ( { { title: '单位', dataIndex: 'unit', - width: '15%', render: (_, __, index) => ( { }, { title: '操作', - width: '10%', render: (_, __, index, { remove }) => (
{ 新增服务类型 -
+
{ { title: '单位名称', dataIndex: ['attributes', 'name'], - width: '60%', render: (text, record) => { const isEditing = record.id === editingKey; return isEditing ? ( @@ -139,7 +138,6 @@ const UnitManagement = () => { }, { title: '操作', - width: '40%', render: (_, record) => { const isEditing = record.id === editingKey; return isEditing ? ( @@ -235,6 +233,7 @@ const UnitManagement = () => {
{ dataIndex: 'created_at', key: 'created_at', sorter: true, - width: 180, render: (text) => ( {new Date(text).toLocaleString('zh-CN', { year: 'numeric', @@ -88,7 +87,7 @@ const SupplierPage = () => { { title: '操作', key: 'action', - width: 150, + fixed: 'right', render: (_, record) => (
{ }, { title: "操作", - width: 100, + render: (_, record, index) => (
{ }, { title: "操作", - width: 250, + fixed: 'right', key: "action", render: (_, record) => ( @@ -146,6 +146,7 @@ const ResourceTask = () => { } >
{ const loadMemberships = async () => { try { setLoading(true); - const { data } = await supabaseService.get('team_membership', { + const { data } = await SupabaseService.select('team_membership', { select: ` id, role, diff --git a/src/pages/resource/team/components/MembershipTable.jsx b/src/pages/resource/team/components/MembershipTable.jsx index b002c6a..dfddfd8 100644 --- a/src/pages/resource/team/components/MembershipTable.jsx +++ b/src/pages/resource/team/components/MembershipTable.jsx @@ -149,6 +149,7 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => { */}
{ // 获取团队成员 const getTeamMembers = async (teamId) => { try { - const result = await supabaseService.get('team_memberships', { + const result = await SupabaseService.select('team_memberships', { select: '*', relations: { user: 'id, email, name' diff --git a/src/utils/menuUtils.jsx b/src/utils/menuUtils.jsx index 9de9046..9e48501 100644 --- a/src/utils/menuUtils.jsx +++ b/src/utils/menuUtils.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { routes } from '@/routes/routes'; import * as AntIcons from '@ant-design/icons'; -import { ColorIcon } from '@/components/common/ColorIcon'; +import { ColorIcon } from '@/components/Layout/ColorIcon';