frontend: add Integration manager actions (remove/sync) and Guilds UI
This commit is contained in:
parent
71994e0c09
commit
7702d3711b
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
55
modern/frontend/src/Guilds.jsx
Normal file
55
modern/frontend/src/Guilds.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user