Compare commits
2 Commits
2c35fe53b9
...
2a756e0980
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a756e0980 | |||
| 664044bcd6 |
+25839
File diff suppressed because one or more lines are too long
@@ -1,18 +1,36 @@
|
||||
{
|
||||
"version": 1,
|
||||
"skills": {
|
||||
"frontend-design": {
|
||||
"source": "anthropics/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/frontend-design/SKILL.md",
|
||||
"computedHash": "063a0e6448123cd359ad0044cc46b0e490cc7964d45ef4bb9fd842bd2ffbca67"
|
||||
},
|
||||
"grill-me": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/productivity/grill-me/SKILL.md",
|
||||
"computedHash": "784f0dbb7403b0f00324bce9a112f715342777a0daee7bbb7385f9c6f0a170ea"
|
||||
},
|
||||
"improve-codebase-architecture": {
|
||||
"source": "mattpocock/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/engineering/improve-codebase-architecture/SKILL.md",
|
||||
"computedHash": "ef32aea0a8fab9b365ff9e08a95f8d353e20ca21ea46ec2e73587c86dd341351"
|
||||
},
|
||||
"pptx": {
|
||||
"source": "anthropics/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/pptx/SKILL.md",
|
||||
"computedHash": "6b8b859d26f93aa059c9870d1ab76b44ab69e3d6757ce4a03cc94cb760888073"
|
||||
},
|
||||
"shadcn": {
|
||||
"source": "shadcn/ui",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/shadcn/SKILL.md",
|
||||
"computedHash": "80a6226e78f6d1fe464214ae0ef449d49d8ffaa3e7704f011e9b418c678ad4d1"
|
||||
},
|
||||
"supabase": {
|
||||
"source": "supabase/agent-skills",
|
||||
"sourceType": "github",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState, useCallback, useRef } from 'react'
|
||||
import { supabase } from '@/lib/supabase'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
export function useSkillLevels() {
|
||||
const [levels, setLevels] = useState({})
|
||||
@@ -40,19 +41,22 @@ export function useSkillLevels() {
|
||||
const oldLevel = existing?.level
|
||||
|
||||
if (existing) {
|
||||
await supabase.from('skill_levels').update({ level: newLevel }).eq('id', existing.id)
|
||||
const { error } = await supabase.from('skill_levels').update({ level: newLevel }).eq('id', existing.id)
|
||||
if (error) { toast.error(error.message); return }
|
||||
} else {
|
||||
await supabase.from('skill_levels').insert({ member_id: memberId, skill_id: skillId, level: newLevel })
|
||||
const { error } = await supabase.from('skill_levels').insert({ member_id: memberId, skill_id: skillId, level: newLevel })
|
||||
if (error) { toast.error(error.message); return }
|
||||
}
|
||||
|
||||
if (oldLevel !== newLevel && changedBy) {
|
||||
await supabase.from('skill_history').insert({
|
||||
const { error } = await supabase.from('skill_history').insert({
|
||||
member_id: memberId,
|
||||
skill_id: skillId,
|
||||
old_level: oldLevel || null,
|
||||
new_level: newLevel,
|
||||
changed_by: changedBy,
|
||||
})
|
||||
if (error) { toast.error(error.message); return }
|
||||
}
|
||||
|
||||
await fetch()
|
||||
|
||||
@@ -58,6 +58,8 @@ export function AcceptInvite() {
|
||||
}
|
||||
|
||||
await supabase.from('invitations').update({ accepted: true }).eq('token', token)
|
||||
.then()
|
||||
.catch(() => {})
|
||||
|
||||
toast.success('Compte créé ! Vous pouvez vous connecter.')
|
||||
navigate('/login')
|
||||
|
||||
@@ -25,7 +25,8 @@ export function Members() {
|
||||
}
|
||||
|
||||
async function updateMember() {
|
||||
await supabase.from('members').update({ full_name: editName }).eq('id', editMember.id)
|
||||
const { error } = await supabase.from('members').update({ full_name: editName }).eq('id', editMember.id)
|
||||
if (error) { toast.error(error.message); return }
|
||||
setEditMember(null)
|
||||
load()
|
||||
toast.success('Membre mis à jour')
|
||||
@@ -33,7 +34,8 @@ export function Members() {
|
||||
|
||||
async function deleteMember(id) {
|
||||
if (!confirm('Supprimer ce membre ?')) return
|
||||
await supabase.from('members').delete().eq('id', id)
|
||||
const { error } = await supabase.from('members').delete().eq('id', id)
|
||||
if (error) { toast.error(error.message); return }
|
||||
load()
|
||||
toast.success('Membre supprimé')
|
||||
}
|
||||
|
||||
@@ -22,9 +22,11 @@ export function Skills() {
|
||||
|
||||
async function saveCategory() {
|
||||
if (editCat) {
|
||||
await supabase.from('categories').update({ name: newCatName, color: newCatColor }).eq('id', editCat)
|
||||
const { error } = await supabase.from('categories').update({ name: newCatName, color: newCatColor }).eq('id', editCat)
|
||||
if (error) { toast.error(error.message); return }
|
||||
} else {
|
||||
await supabase.from('categories').insert({ name: newCatName, color: newCatColor })
|
||||
const { error } = await supabase.from('categories').insert({ name: newCatName, color: newCatColor })
|
||||
if (error) { toast.error(error.message); return }
|
||||
}
|
||||
setCatDialogOpen(false)
|
||||
setEditCat(null)
|
||||
@@ -40,7 +42,8 @@ export function Skills() {
|
||||
}
|
||||
|
||||
async function saveSkill() {
|
||||
await supabase.from('skills').insert({ name: newSkill.name, category_id: newSkill.category_id })
|
||||
const { error } = await supabase.from('skills').insert({ name: newSkill.name, category_id: newSkill.category_id })
|
||||
if (error) { toast.error(error.message); return }
|
||||
setSkillDialogOpen(false)
|
||||
setNewSkill({ name: '', category_id: '' })
|
||||
refetchSkills()
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
-- ============================================================
|
||||
-- Migration 002: RLS WITH CHECK + GIN indexes + level_descriptions policies
|
||||
-- ============================================================
|
||||
|
||||
-- 1. Ajout WITH CHECK sur les policies UPDATE existantes
|
||||
-- (fonctionnellement identique au USING, mais explicite)
|
||||
|
||||
ALTER POLICY "categories_update_admin" ON categories
|
||||
RENAME TO "categories_update_admin_old";
|
||||
CREATE POLICY "categories_update_admin" ON categories FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
) WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
);
|
||||
DROP POLICY "categories_update_admin_old" ON categories;
|
||||
|
||||
ALTER POLICY "skills_update_admin" ON skills
|
||||
RENAME TO "skills_update_admin_old";
|
||||
CREATE POLICY "skills_update_admin" ON skills FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
) WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
);
|
||||
DROP POLICY "skills_update_admin_old" ON skills;
|
||||
|
||||
ALTER POLICY "members_update_admin" ON members
|
||||
RENAME TO "members_update_admin_old";
|
||||
CREATE POLICY "members_update_admin" ON members FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
) WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
);
|
||||
DROP POLICY "members_update_admin_old" ON members;
|
||||
|
||||
ALTER POLICY "skill_levels_update_admin" ON skill_levels
|
||||
RENAME TO "skill_levels_update_admin_old";
|
||||
CREATE POLICY "skill_levels_update_admin" ON skill_levels FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
) WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
);
|
||||
DROP POLICY "skill_levels_update_admin_old" ON skill_levels;
|
||||
|
||||
ALTER POLICY "invitations_update_admin" ON invitations
|
||||
RENAME TO "invitations_update_admin_old";
|
||||
CREATE POLICY "invitations_update_admin" ON invitations FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
) WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
);
|
||||
DROP POLICY "invitations_update_admin_old" ON invitations;
|
||||
|
||||
-- 2. Filtre expiration sur invitations_read_admin
|
||||
|
||||
ALTER POLICY "invitations_read_admin" ON invitations
|
||||
RENAME TO "invitations_read_admin_old";
|
||||
CREATE POLICY "invitations_read_admin" ON invitations FOR SELECT USING (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
AND expires_at > now()
|
||||
);
|
||||
DROP POLICY "invitations_read_admin_old" ON invitations;
|
||||
|
||||
-- 3. Policies d'écriture pour level_descriptions
|
||||
|
||||
CREATE POLICY "level_descriptions_insert_admin" ON level_descriptions FOR INSERT WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
);
|
||||
CREATE POLICY "level_descriptions_update_admin" ON level_descriptions FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
) WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
);
|
||||
CREATE POLICY "level_descriptions_delete_admin" ON level_descriptions FOR DELETE USING (
|
||||
EXISTS (SELECT 1 FROM members WHERE id = auth.uid() AND role = 'admin')
|
||||
);
|
||||
|
||||
-- 4. Index GIN pour full-text search (stemming français)
|
||||
|
||||
CREATE INDEX idx_skills_name_gin ON skills USING gin(to_tsvector('french', name));
|
||||
CREATE INDEX idx_members_full_name_gin ON members USING gin(to_tsvector('french', full_name));
|
||||
Reference in New Issue
Block a user