frontend: add Integration manager actions (remove/sync) and Guilds UI

This commit is contained in:
TLimoges33 2025-08-28 17:19:46 +00:00
parent 71994e0c09
commit 7702d3711b
3 changed files with 98 additions and 3 deletions

View File

@ -1,5 +1,6 @@
import React from 'react'
import Integrations from './Integrations'
import Guilds from './Guilds'
export default function App(){
return (
@ -7,6 +8,7 @@ export default function App(){
<h1>LifeRPG Modern</h1>
<p>Welcome frontend scaffold. Connect to backend at <code>/api/v1</code>.</p>
<Integrations />
<Guilds />
</div>
)
}

View File

@ -0,0 +1,55 @@
import React, {useState, useEffect} from 'react'
const API = (path, opts) => fetch(path, {...(opts||{}), credentials: 'include'}).then(r=>r.json())
export default function Guilds(){
const [guilds, setGuilds] = useState([])
const [name, setName] = useState('')
const [members, setMembers] = useState([])
const [selectedGuild, setSelectedGuild] = useState(null)
const [userId] = useState(1)
useEffect(()=>{ API('/api/v1/guilds').then(setGuilds).catch(()=>setGuilds([])) }, [])
function createGuild(){
if(!name.trim()) return
API('/api/v1/guilds', {method:'POST', body: JSON.stringify({name, owner_id: userId}), headers: {'Content-Type':'application/json'}})
.then(g=> setGuilds([...guilds, g]))
.catch(()=>{})
}
function loadMembers(gid){
API(`/api/v1/guilds/${gid}/members`).then(setMembers).catch(()=>setMembers([]))
setSelectedGuild(gid)
}
function addMember(gid){
const uid = prompt('User ID to add:')
if(!uid) return
API(`/api/v1/guilds/${gid}/members`, {method:'POST', body: JSON.stringify({user_id: parseInt(uid)}), headers: {'Content-Type':'application/json'}})
.then(()=> loadMembers(gid))
.catch(()=> alert('failed'))
}
return (
<div style={{marginTop:20}}>
<h2>Guilds</h2>
<div>
<input value={name} onChange={e=>setName(e.target.value)} placeholder="Guild name" />
<button onClick={createGuild} style={{marginLeft:8}}>Create</button>
</div>
<ul>
{guilds && guilds.length ? guilds.map(g=> (
<li key={g.id}>
<strong>{g.name}</strong> (owner: {g.owner_id}) <button onClick={()=>loadMembers(g.id)}>Members</button>
<button style={{marginLeft:8}} onClick={()=>addMember(g.id)}>Add Member</button>
</li>
)): <li>No guilds</li>}
</ul>
<h3>Members</h3>
<ul>
{members && members.length ? members.map(m=> (<li key={m.id}>User {m.user_id} {m.role}</li>)) : <li>No members</li>}
</ul>
</div>
)
}

View File

@ -6,6 +6,8 @@ export default function Integrations(){
const [integrations, setIntegrations] = useState([])
const [events, setEvents] = useState(null)
const [userId] = useState(1)
const [msg, setMsg] = useState(null)
const [loadingId, setLoadingId] = useState(null)
useEffect(()=>{
API(`/api/v1/users/${userId}/integrations`).then(d=>setIntegrations(d)).catch(()=>setIntegrations([]))
@ -17,7 +19,37 @@ export default function Integrations(){
}
function fetchEvents(integrationId){
API(`/api/v1/integrations/${integrationId}/google/events`).then(d=>setEvents(d)).catch(e=>setEvents({error: String(e)}))
setLoadingId(integrationId)
fetch(`/api/v1/integrations/${integrationId}/google/events`, {credentials:'include'})
.then(r=>r.json())
.then(d=>{
setEvents(d)
setMsg('Fetched events')
})
.catch(e=>setEvents({error: String(e)}))
.finally(()=>setLoadingId(null))
}
function removeIntegration(integrationId){
if(!confirm('Remove integration?')) return
setLoadingId(integrationId)
fetch(`/api/v1/integrations/${integrationId}`, {method: 'DELETE', credentials: 'include'})
.then(r=>r.json())
.then(d=>{
setMsg('Integration removed')
setIntegrations(integrations.filter(i=>i.id !== integrationId))
})
.catch(e=>setMsg('Failed to remove'))
.finally(()=>setLoadingId(null))
}
function syncIntegration(integrationId){
setLoadingId(integrationId)
fetch(`/api/v1/integrations/${integrationId}/sync_to_habits`, {method:'POST', credentials:'include'})
.then(r=>r.json())
.then(d=>setMsg(`Synced ${d.count || 0} items`))
.catch(e=>setMsg('Sync failed'))
.finally(()=>setLoadingId(null))
}
return (
@ -27,11 +59,17 @@ export default function Integrations(){
<h3>Your Integrations</h3>
<ul>
{integrations && integrations.length ? integrations.map(i=> (
<li key={i.id}>
{i.provider} id: {i.id} user: {i.user_id} <button style={{marginLeft:8}} onClick={()=>fetchEvents(i.id)}>Fetch Events</button>
<li key={i.id} style={{marginBottom:8}}>
<strong>{i.provider}</strong> id: {i.id} user: {i.user_id}
<div style={{display:'inline-block', marginLeft:12}}>
<button onClick={()=>fetchEvents(i.id)} disabled={loadingId===i.id} style={{marginRight:6}}>Fetch Events</button>
<button onClick={()=>syncIntegration(i.id)} disabled={loadingId===i.id} style={{marginRight:6}}>Sync Habits</button>
<button onClick={()=>removeIntegration(i.id)} disabled={loadingId===i.id}>Remove</button>
</div>
</li>
)): <li>No integrations</li>}
</ul>
{msg && <div style={{marginTop:8, color:'#0366d6'}}>{msg}</div>}
<h3>Events</h3>
<pre style={{whiteSpace:'pre-wrap',background:'#f6f6f6',padding:10}}>{events? JSON.stringify(events, null, 2): 'No events fetched'}</pre>
</div>