diff --git a/src/config/supabase.js b/src/config/supabase.js
index 6a073da..5f7b4fa 100644
--- a/src/config/supabase.js
+++ b/src/config/supabase.js
@@ -21,7 +21,7 @@ export const createSupabase = () => {
detectSessionInUrl: false,
},
db: {
- schema: 'limq_dev'
+ schema: 'limq'
}
}
);
diff --git a/src/contexts/ThemeContext.jsx b/src/contexts/ThemeContext.jsx
index 511bbbd..b86fcdb 100644
--- a/src/contexts/ThemeContext.jsx
+++ b/src/contexts/ThemeContext.jsx
@@ -1,5 +1,5 @@
-import React, { createContext, useContext, useState, useEffect } from 'react';
-
+import React, { createContext,useContext, useState, useEffect, } from 'react';
+import { ConfigProvider, theme } from "antd";
const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
@@ -19,7 +19,52 @@ export const ThemeProvider = ({ children }) => {
return (
- {children}
+
+ { children }
+
);
};
diff --git a/src/hooks/supabaseService.js b/src/hooks/supabaseService.js
new file mode 100644
index 0000000..8395c97
--- /dev/null
+++ b/src/hooks/supabaseService.js
@@ -0,0 +1,101 @@
+import { supabase } from '@/config/supabase'
+
+class SupabaseService {
+ async get(table, options = {}) {
+ try {
+ let query = supabase
+ .from(table)
+ .select(options.select || '*', { count: 'exact' })
+
+ // 处理精确匹配条件
+ if (options.match) {
+ query = query.match(options.match)
+ }
+
+ // 处理过滤条件
+ if (options.filter) {
+ Object.entries(options.filter).forEach(([key, condition]) => {
+ Object.entries(condition).forEach(([operator, value]) => {
+ query = query.filter(key, operator, value)
+ })
+ })
+ }
+
+ // 处理排序
+ if (options.order) {
+ query = query.order(options.order.column, {
+ ascending: options.order.ascending
+ })
+ }
+
+ // 处理分页
+ if (options.page && options.pageSize) {
+ const from = (options.page - 1) * options.pageSize
+ const to = from + options.pageSize - 1
+ query = query.range(from, to)
+ }
+
+ const { data, error, count } = await query
+
+ if (error) throw error
+ return {
+ data,
+ total: count || 0
+ }
+ } catch (error) {
+ console.error(`Error fetching from ${table}:`, error.message)
+ throw error
+ }
+ }
+
+ // 通用 INSERT 请求
+ async insert(table, data) {
+ try {
+ const { data: result, error } = await supabase
+ .from(table)
+ .insert(data)
+ .select()
+
+ if (error) throw error
+ return result
+ } catch (error) {
+ console.error(`Error inserting into ${table}:`, error.message)
+ throw error
+ }
+ }
+
+ // 通用 UPDATE 请求
+ async update(table, match, updates) {
+ try {
+ const { data, error } = await supabase
+ .from(table)
+ .update(updates)
+ .match(match)
+ .select()
+
+ if (error) throw error
+ return data
+ } catch (error) {
+ console.error(`Error updating ${table}:`, error.message)
+ throw error
+ }
+ }
+
+ // 通用 DELETE 请求
+ async delete(table, match) {
+ try {
+ const { error } = await supabase
+ .from(table)
+ .delete()
+ .match(match)
+
+ if (error) throw error
+ return true
+ } catch (error) {
+ console.error(`Error deleting from ${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 aed400d..4804116 100644
--- a/src/hooks/team/useTeamMemberships.js
+++ b/src/hooks/team/useTeamMemberships.js
@@ -1,71 +1,58 @@
import { useState, useCallback } from 'react';
-import { message } from 'antd';
-import { teamMembershipService } from '@/services/supabase/teamMembership';
+import { supabaseService } from '@/hooks/supabaseService';
-export const useTeamMemberships = (teamId) => {
- const [memberships, setMemberships] = useState([]);
+export const useTeamMembership = (teamId) => {
const [loading, setLoading] = useState(false);
+ const [memberships, setMemberships] = useState([]);
- const fetchMemberships = useCallback(async () => {
+ const loadMemberships = useCallback(async () => {
if (!teamId) return;
+ setLoading(true);
try {
- setLoading(true);
- const data = await teamMembershipService.getMemberships(teamId);
- setMemberships(data);
+ const result = await supabaseService.get('team_memberships', {
+ select: '*',
+ relations: {
+ user: 'id, email, name'
+ },
+ match: { teamId }
+ });
+ setMemberships(result.data || []);
} catch (error) {
- message.error('Failed to fetch team memberships');
- console.error(error);
+ console.error('获取成员列表失败:', error);
} finally {
setLoading(false);
}
}, [teamId]);
- const createMembership = async (values) => {
- try {
- const newMembership = await teamMembershipService.createMembership({
- ...values,
- teamId,
- });
- setMemberships(prev => [...prev, newMembership]);
- message.success('Member added successfully');
- return newMembership;
- } catch (error) {
- message.error('Failed to add member');
- throw error;
- }
+ const addMembership = async (values) => {
+ const result = await supabaseService.insert('team_memberships', {
+ ...values,
+ teamId
+ });
+ await loadMemberships();
+ return result[0];
};
const updateMembership = async (id, values) => {
- try {
- const updatedMembership = await teamMembershipService.updateMembership(id, values);
- setMemberships(prev => prev.map(membership =>
- membership.id === id ? updatedMembership : membership
- ));
- message.success('Member updated successfully');
- return updatedMembership;
- } catch (error) {
- message.error('Failed to update member');
- throw error;
- }
+ const result = await supabaseService.update('team_memberships',
+ { id },
+ values
+ );
+ await loadMemberships();
+ return result[0];
};
const deleteMembership = async (id) => {
- try {
- await teamMembershipService.deleteMembership(id);
- setMemberships(prev => prev.filter(membership => membership.id !== id));
- message.success('Member removed successfully');
- } catch (error) {
- message.error('Failed to remove member');
- throw error;
- }
+ await supabaseService.delete('team_memberships', { id });
+ await loadMemberships();
};
return {
- memberships,
loading,
- fetchMemberships,
- createMembership,
+ memberships,
+ loadMemberships,
+ addMembership,
updateMembership,
deleteMembership,
};
diff --git a/src/hooks/team/useTeams.js b/src/hooks/team/useTeams.js
index b6bc094..b4d8f80 100644
--- a/src/hooks/team/useTeams.js
+++ b/src/hooks/team/useTeams.js
@@ -1,68 +1,96 @@
-import { useState, useCallback } from 'react';
-import { message } from 'antd';
-import { teamService } from '@/services/supabase/team';
+import { supabaseService } from '@/hooks/supabaseService';
-export const useTeams = (pagination, sorter) => {
- const [teams, setTeams] = useState([]);
- const [loading, setLoading] = useState(false);
-
- const fetchTeams = useCallback(async (params = {}) => {
+export const useTeams = () => {
+ // 获取团队列表
+ const fetchTeams = async (params = {}) => {
try {
- setLoading(true);
- const { data, total } = await teamService.getTeams({
- page: params.current || pagination.current,
- pageSize: params.pageSize || pagination.pageSize,
- orderBy: params.field || sorter.field,
- ascending: params.order === 'ascend',
- ...(params?.search!==''?{searchQuery:params.search}:{})
+ const result = await supabaseService.get('teams', {
+ select: `
+ id,
+ name,
+ description,
+ attributes,
+ created_at,
+ updated_at,
+ avatar_url,
+ team_membership!inner (
+ id,
+ user_id,
+ role,
+ is_creator,
+ users (
+ id,
+ email
+ )
+ )
+ `,
+ filter: {
+ deleted_at: { is: null },
+ ...(params.search ? { name: { ilike: `%${params.search}%` } } : {})
+ },
+ order: {
+ column: params.field || 'created_at',
+ ascending: params.order === 'ascend'
+ },
+ page: params.current || 1,
+ pageSize: params.pageSize || 10
});
- setTeams(data);
- return { data, total };
-
+ return result;
} catch (error) {
- console.error(error);
- message.error('获取团队列表失败');
- } finally {
- setLoading(false);
+ console.error('获取团队列表失败:', error);
+ throw error;
}
- }, [pagination.current, pagination.pageSize, sorter.field, sorter.order]);
+ };
+ // 创建团队
const createTeam = async (values) => {
try {
- const newTeam = await teamService.createTeam(values);
- setTeams(prev => [...prev, newTeam]);
- return newTeam;
+ const newTeam = await supabaseService.insert('teams', {
+ name: values.name,
+ description: values.description
+ });
+
+ // 创建团队成员关系
+ await supabaseService.insert('team_membership', {
+ team_id: newTeam[0].id,
+ user_id: values.userId,
+ role: 'OWNER',
+ is_creator: true,
+ id: newTeam[0].id
+ });
+
+ return newTeam[0];
} catch (error) {
- message.error('Failed to create team');
+ console.error('创建团队失败:', error);
throw error;
}
};
+ // 更新团队
const updateTeam = async (id, values) => {
try {
- const updatedTeam = await teamService.updateTeam(id, values);
- setTeams(prev => prev.map(team =>
- team.id === id ? updatedTeam : team
- ));
- return updatedTeam;
+ const result = await supabaseService.update('teams',
+ { id },
+ values
+ );
+ return result[0];
} catch (error) {
- message.error('Failed to update team');
+ console.error('更新团队失败:', error);
throw error;
}
};
+ // 删除团队
const deleteTeam = async (id) => {
try {
- await teamService.deleteTeam(id);
- setTeams(prev => prev.filter(team => team.id !== id));
+ await supabaseService.delete('teams', { id });
} catch (error) {
+ console.error('删除团队失败:', error);
throw error;
}
};
return {
- teams,
- loading,
fetchTeams,
createTeam,
updateTeam,
diff --git a/src/pages/auth/Login.jsx b/src/pages/auth/Login.jsx
index 654dcc3..ce06cc9 100644
--- a/src/pages/auth/Login.jsx
+++ b/src/pages/auth/Login.jsx
@@ -1,8 +1,9 @@
import React from "react";
import { Form, Input, Button, Divider, message } from "antd";
-import { GoogleOutlined } from "@ant-design/icons";
+import { GoogleOutlined, MailOutlined, LockOutlined } from "@ant-design/icons";
import { Link, useNavigate } from "react-router-dom";
import { useAuth } from "@/contexts/AuthContext";
+import { supabaseService } from "@/hooks/supabaseService";
const Login = () => {
const navigate = useNavigate();
@@ -10,92 +11,100 @@ const Login = () => {
const [form] = Form.useForm();
const handleLogin = async (values) => {
- try {
- await login(values.email, values.password);
- message.success("登录成功!");
- navigate("/");
- } catch (error) {
- console.error("Login error:", error);
- }
+ login(values.email, values.password);
};
- const handleGoogleLogin = async () => {
- try {
- await signInWithGoogle();
- } catch (error) {
- console.error("Google login error:", error);
- }
- };
return (
-
-
-
-
-
- Uppeta
-
-
欢迎回来!请登录您的账户。
-
-
-
-
-
-
-
-
-
-
-
-
-
- 忘记密码?
-
-
-
-
-
-
-
-
-
或
-
-
}
- block
- onClick={handleGoogleLogin}
- className="mb-6"
- loading={loading}
- >
- 使用 Google 账号登录
-
-
+
+
+ {/* 左侧登录表单 */}
+
+
+
+ Uppeta
+
+
+ 欢迎回来!请登录您的账户
+
-
+
+ }
+ placeholder="邮箱"
+ className="h-12 rounded-xl dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100"
+ />
+
+
+
+ }
+ placeholder="密码"
+ className="h-12 rounded-xl dark:bg-gray-700 dark:border-gray-600 dark:text-gray-100"
+ />
+
+
+
+
+ 忘记密码?
+
+
+
+
+
+
+
+
+ 或
+
+
+
}
+ block
+ onClick={signInWithGoogle}
+ loading={loading}
+ 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/resource/team/components/CreateTeamModal/TeamForm.jsx b/src/pages/resource/team/components/CreateTeamModal/TeamForm.jsx
index 357c1a9..4c450b8 100644
--- a/src/pages/resource/team/components/CreateTeamModal/TeamForm.jsx
+++ b/src/pages/resource/team/components/CreateTeamModal/TeamForm.jsx
@@ -67,22 +67,7 @@ export const TeamForm = ({ form }) => {
-
-
-
+
);
};
\ No newline at end of file
diff --git a/src/pages/resource/team/components/ExpandedMemberships.jsx b/src/pages/resource/team/components/ExpandedMemberships.jsx
index 2bd7910..2a3b2bf 100644
--- a/src/pages/resource/team/components/ExpandedMemberships.jsx
+++ b/src/pages/resource/team/components/ExpandedMemberships.jsx
@@ -1,24 +1,72 @@
-import React, { useState } from 'react';
-import { Modal, Form, Input, Select, message } from 'antd';
+import React, { useState, useEffect } from 'react';
+import { Modal,Button, Form, Input, Select, message } from 'antd';
import { MembershipTable } from './MembershipTable';
+import { supabaseService } from '@/hooks/supabaseService';
const { Option } = Select;
-export const ExpandedMemberships = ({ teamId, memberships: initialMemberships }) => {
- const [memberships, setMemberships] = useState(initialMemberships);
+export const ExpandedMemberships = ({ teamId }) => {
+ const [memberships, setMemberships] = useState([]);
const [isModalVisible, setIsModalVisible] = useState(false);
const [form] = Form.useForm();
+ const [loading, setLoading] = useState(false);
- const handleUpdate = (id, values) => {
- setMemberships(prev =>
- prev.map(item => item.id === id ? { ...item, ...values } : item)
- );
- message.success('成员信息已更新');
+ // 加载团队成员
+ const loadMemberships = async () => {
+ try {
+ setLoading(true);
+ const { data } = await supabaseService.get('team_membership', {
+ select: `
+ id,
+ role,
+ is_creator,
+ user:users (
+ id,
+ email
+ )
+ `,
+ match: { team_id: teamId }
+ });
+ setMemberships(data || []);
+ } catch (error) {
+ message.error('获取团队成员失败');
+ console.error('Failed to fetch memberships:', error);
+ } finally {
+ setLoading(false);
+ }
};
- const handleDelete = (id) => {
- setMemberships(prev => prev.filter(item => item.id !== id));
- message.success('成员已删除');
+ useEffect(() => {
+ if (teamId) {
+ loadMemberships();
+ }
+ }, [teamId]);
+
+ const handleUpdate = async (id, values) => {
+ try {
+ await supabaseService.update('team_membership',
+ { id },
+ {
+ role: values.role
+ }
+ );
+ message.success('成员角色已更新');
+ await loadMemberships();
+ } catch (error) {
+ message.error('更新成员角色失败');
+ console.error('Update failed:', error);
+ }
+ };
+
+ const handleDelete = async (id) => {
+ try {
+ await supabaseService.delete('team_membership', { id });
+ message.success('成员已删除');
+ await loadMemberships();
+ } catch (error) {
+ message.error('删除成员失败');
+ console.error('Delete failed:', error);
+ }
};
const handleAdd = () => {
@@ -29,16 +77,18 @@ export const ExpandedMemberships = ({ teamId, memberships: initialMemberships })
const handleModalOk = async () => {
try {
const values = await form.validateFields();
- const newMember = {
- id: `${Date.now()}`,
- teamId,
- isCreator: false,
- ...values,
- };
- setMemberships(prev => [...prev, newMember]);
+ await supabaseService.insert('team_membership', {
+ team_id: teamId,
+ user_id: values.user_id,
+ role: values.role,
+ is_creator: false
+ });
+
setIsModalVisible(false);
message.success('成员已添加');
+ await loadMemberships();
} catch (error) {
+ message.error('添加成员失败');
console.error('Add failed:', error);
}
};
@@ -46,53 +96,14 @@ export const ExpandedMemberships = ({ teamId, memberships: initialMemberships })
return (
团队成员
-
- setIsModalVisible(false)}
- >
-
-
-
-
-
-
-
-
-
-
-
);
};
\ No newline at end of file
diff --git a/src/pages/resource/team/components/MembershipTable.jsx b/src/pages/resource/team/components/MembershipTable.jsx
index 8b5b324..b002c6a 100644
--- a/src/pages/resource/team/components/MembershipTable.jsx
+++ b/src/pages/resource/team/components/MembershipTable.jsx
@@ -32,13 +32,13 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
const columns = [
{
title: '成员',
- dataIndex: 'users',
- key: 'users',
+ dataIndex: 'user',
+ key: 'user',
editable: true,
- render: (users) => (
+ render: (user) => (
- {users.email}
- {users.email}
+ {user?.email}
+ {user?.email}
),
},
@@ -139,14 +139,14 @@ export const MembershipTable = ({ memberships, onUpdate, onDelete, onAdd }) => {
return (
-
}
onClick={onAdd}
className="mb-4"
>
添加成员
-
+ */}