fix(geplante-nachrichten): add /api prefix to all API paths, fix subscribe room token, unmask empty bot credentials, add Einzelnachrichten tab
This commit is contained in:
@@ -15,12 +15,22 @@ import {
|
||||
TableRow,
|
||||
Paper,
|
||||
IconButton,
|
||||
Tab,
|
||||
Tabs,
|
||||
TextField,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
CircularProgress,
|
||||
Divider,
|
||||
} from '@mui/material';
|
||||
import { Add as AddIcon, Edit as EditIcon } from '@mui/icons-material';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { Add as AddIcon, Edit as EditIcon, Delete as DeleteIcon, Send as SendIcon } from '@mui/icons-material';
|
||||
import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import DashboardLayout from '../components/dashboard/DashboardLayout';
|
||||
import { usePermissionContext } from '../contexts/PermissionContext';
|
||||
import { useNotification } from '../contexts/NotificationContext';
|
||||
import { scheduledMessagesApi } from '../services/scheduledMessages';
|
||||
import type { MessageType, ScheduledMessageRule } from '../types/scheduledMessages.types';
|
||||
|
||||
@@ -54,12 +64,211 @@ function triggerSummary(rule: ScheduledMessageRule): string {
|
||||
return TRIGGER_LABELS[rule.trigger_mode] ?? rule.trigger_mode;
|
||||
}
|
||||
|
||||
function formatSendAt(iso: string): string {
|
||||
return new Date(iso).toLocaleString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
}
|
||||
|
||||
function EinzelNachrichtenTab() {
|
||||
const queryClient = useQueryClient();
|
||||
const { showSuccess, showError } = useNotification();
|
||||
|
||||
const { data: messagesData, isLoading: messagesLoading, isError: messagesError } = useQuery({
|
||||
queryKey: ['scheduled-one-time-messages'],
|
||||
queryFn: scheduledMessagesApi.getOneTimeMessages,
|
||||
});
|
||||
|
||||
const { data: roomsData, isLoading: roomsLoading } = useQuery({
|
||||
queryKey: ['scheduled-messages-rooms'],
|
||||
queryFn: scheduledMessagesApi.getRooms,
|
||||
});
|
||||
|
||||
const [message, setMessage] = useState('');
|
||||
const [roomToken, setRoomToken] = useState('');
|
||||
const [sendAt, setSendAt] = useState('');
|
||||
|
||||
const rooms = roomsData?.data ?? [];
|
||||
const roomsConfigured = roomsData?.configured ?? false;
|
||||
const roomsErr = roomsData?.error;
|
||||
|
||||
const createMutation = useMutation({
|
||||
mutationFn: () => {
|
||||
const room = rooms.find(r => r.token === roomToken);
|
||||
return scheduledMessagesApi.createOneTimeMessage({
|
||||
message,
|
||||
target_room_token: roomToken,
|
||||
target_room_name: room?.displayName,
|
||||
send_at: new Date(sendAt).toISOString(),
|
||||
});
|
||||
},
|
||||
onSuccess: () => {
|
||||
showSuccess('Einzelnachricht geplant');
|
||||
setMessage('');
|
||||
setRoomToken('');
|
||||
setSendAt('');
|
||||
queryClient.invalidateQueries({ queryKey: ['scheduled-one-time-messages'] });
|
||||
},
|
||||
onError: () => showError('Fehler beim Speichern'),
|
||||
});
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: (id: string) => scheduledMessagesApi.deleteOneTimeMessage(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['scheduled-one-time-messages'] });
|
||||
},
|
||||
onError: () => showError('Fehler beim Löschen'),
|
||||
});
|
||||
|
||||
const canCreate = !!message.trim() && !!roomToken && !!sendAt;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Paper sx={{ p: 3, mb: 3 }}>
|
||||
<Typography variant="subtitle1" gutterBottom fontWeight={500}>Neue Einzelnachricht</Typography>
|
||||
<Divider sx={{ mb: 2 }} />
|
||||
|
||||
{/* Room picker */}
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<FormControl fullWidth size="small">
|
||||
<FormLabel sx={{ mb: 0.5 }}>Zielraum</FormLabel>
|
||||
{roomsLoading ? (
|
||||
<CircularProgress size={20} />
|
||||
) : !roomsConfigured ? (
|
||||
<Alert severity="warning" sx={{ mt: 1 }}>
|
||||
Bot-Konto nicht konfiguriert. Bitte in den Admin-Einstellungen unter Nextcloud konfigurieren.
|
||||
</Alert>
|
||||
) : roomsErr ? (
|
||||
<Alert severity="error" sx={{ mt: 1 }}>
|
||||
Nextcloud nicht erreichbar — Raumliste kann nicht geladen werden.
|
||||
</Alert>
|
||||
) : (
|
||||
<Select
|
||||
value={roomToken}
|
||||
onChange={(e) => setRoomToken(e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value="" disabled>Raum auswählen...</MenuItem>
|
||||
{rooms.map((room) => (
|
||||
<MenuItem key={room.token} value={room.token}>
|
||||
{room.displayName}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
{/* Send time */}
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
label="Sendezeitpunkt"
|
||||
type="datetime-local"
|
||||
value={sendAt}
|
||||
onChange={(e) => setSendAt(e.target.value)}
|
||||
size="small"
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Message */}
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
label="Nachricht"
|
||||
value={message}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
multiline
|
||||
minRows={3}
|
||||
maxRows={8}
|
||||
fullWidth
|
||||
size="small"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={createMutation.isPending ? <CircularProgress size={16} /> : <SendIcon />}
|
||||
onClick={() => createMutation.mutate()}
|
||||
disabled={!canCreate || createMutation.isPending || !roomsConfigured}
|
||||
>
|
||||
Planen
|
||||
</Button>
|
||||
</Paper>
|
||||
|
||||
{/* Pending messages list */}
|
||||
<Typography variant="subtitle1" gutterBottom fontWeight={500}>Ausstehende Einzelnachrichten</Typography>
|
||||
|
||||
{messagesError && (
|
||||
<Alert severity="error" sx={{ mb: 2 }}>Fehler beim Laden der Nachrichten.</Alert>
|
||||
)}
|
||||
|
||||
{messagesLoading ? (
|
||||
<TableContainer component={Paper}>
|
||||
<Table size="small">
|
||||
<TableBody>
|
||||
{[1, 2].map((i) => (
|
||||
<TableRow key={i}>
|
||||
{[1, 2, 3, 4].map((j) => <TableCell key={j}><Skeleton /></TableCell>)}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
) : !messagesData?.data?.length ? (
|
||||
<Typography color="text.secondary" sx={{ py: 2 }}>
|
||||
Keine ausstehenden Einzelnachrichten.
|
||||
</Typography>
|
||||
) : (
|
||||
<TableContainer component={Paper}>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Nachricht</TableCell>
|
||||
<TableCell>Zielraum</TableCell>
|
||||
<TableCell>Sendezeitpunkt</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{messagesData.data.map((msg) => (
|
||||
<TableRow key={msg.id}>
|
||||
<TableCell sx={{ maxWidth: 300, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
{msg.message}
|
||||
</TableCell>
|
||||
<TableCell>{msg.target_room_name ?? msg.target_room_token}</TableCell>
|
||||
<TableCell>{formatSendAt(msg.send_at)}</TableCell>
|
||||
<TableCell padding="checkbox">
|
||||
<IconButton
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={() => deleteMutation.mutate(msg.id)}
|
||||
disabled={deleteMutation.isPending}
|
||||
>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GeplanteMachrichten() {
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
const { hasPermission } = usePermissionContext();
|
||||
const canEdit = hasPermission('scheduled_messages:edit');
|
||||
|
||||
const [tab, setTab] = useState(0);
|
||||
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ['scheduled-messages'],
|
||||
queryFn: scheduledMessagesApi.getAll,
|
||||
@@ -85,7 +294,7 @@ export default function GeplanteMachrichten() {
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||
<Typography variant="h5">Geplante Nachrichten</Typography>
|
||||
{canEdit && (
|
||||
{canEdit && tab === 0 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddIcon />}
|
||||
@@ -96,121 +305,132 @@ export default function GeplanteMachrichten() {
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{isError && (
|
||||
<Alert severity="error" sx={{ mb: 2 }}>
|
||||
Fehler beim Laden der Regeln.
|
||||
</Alert>
|
||||
<Tabs value={tab} onChange={(_, v) => setTab(v)} sx={{ mb: 2 }}>
|
||||
<Tab label="Regeln" />
|
||||
{canEdit && <Tab label="Einzelnachrichten" />}
|
||||
</Tabs>
|
||||
|
||||
{tab === 0 && (
|
||||
<>
|
||||
{isError && (
|
||||
<Alert severity="error" sx={{ mb: 2 }}>
|
||||
Fehler beim Laden der Regeln.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{isLoading ? (
|
||||
<TableContainer component={Paper}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Typ</TableCell>
|
||||
<TableCell>Trigger</TableCell>
|
||||
<TableCell>Zielraum</TableCell>
|
||||
<TableCell>Abonnierbar</TableCell>
|
||||
<TableCell>Aktiv</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<TableRow key={i}>
|
||||
{[1, 2, 3, 4, 5, 6, 7].map((j) => (
|
||||
<TableCell key={j}><Skeleton /></TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
) : rules.length === 0 ? (
|
||||
<Typography sx={{ textAlign: 'center', py: 4 }} color="text.secondary">
|
||||
Keine Regeln konfiguriert
|
||||
</Typography>
|
||||
) : (
|
||||
<TableContainer component={Paper}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Typ</TableCell>
|
||||
<TableCell>Trigger</TableCell>
|
||||
<TableCell>Zielraum</TableCell>
|
||||
<TableCell>Abonnierbar</TableCell>
|
||||
<TableCell>Aktiv</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rules.map((rule) => (
|
||||
<TableRow
|
||||
key={rule.id}
|
||||
hover
|
||||
sx={{ cursor: 'pointer' }}
|
||||
onClick={() => navigate(`/geplante-nachrichten/${rule.id}`)}
|
||||
>
|
||||
<TableCell>{rule.name}</TableCell>
|
||||
<TableCell>
|
||||
<Chip
|
||||
label={MESSAGE_TYPE_LABELS[rule.message_type] ?? rule.message_type}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>{triggerSummary(rule)}</TableCell>
|
||||
<TableCell>{rule.target_room_name ?? rule.target_room_token}</TableCell>
|
||||
<TableCell>{rule.subscribable ? 'Ja' : 'Nein'}</TableCell>
|
||||
<TableCell>
|
||||
<Switch
|
||||
checked={rule.active}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onChange={() => handleToggleActive(rule)}
|
||||
size="small"
|
||||
disabled={!canEdit}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box sx={{ display: 'flex', gap: 0.5 }} onClick={(e) => e.stopPropagation()}>
|
||||
{canEdit && (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => navigate(`/geplante-nachrichten/${rule.id}/bearbeiten`)}
|
||||
>
|
||||
<EditIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
{canEdit && (
|
||||
deleteConfirmId === rule.id ? (
|
||||
<Button
|
||||
size="small"
|
||||
color="error"
|
||||
variant="contained"
|
||||
onClick={() => handleDelete(rule.id)}
|
||||
>
|
||||
Löschen?
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={() => setDeleteConfirmId(rule.id)}
|
||||
>
|
||||
Löschen
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isLoading ? (
|
||||
<TableContainer component={Paper}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Typ</TableCell>
|
||||
<TableCell>Trigger</TableCell>
|
||||
<TableCell>Zielraum</TableCell>
|
||||
<TableCell>Abonnierbar</TableCell>
|
||||
<TableCell>Aktiv</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<TableRow key={i}>
|
||||
{[1, 2, 3, 4, 5, 6, 7].map((j) => (
|
||||
<TableCell key={j}><Skeleton /></TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
) : rules.length === 0 ? (
|
||||
<Typography sx={{ textAlign: 'center', py: 4 }} color="text.secondary">
|
||||
Keine Regeln konfiguriert
|
||||
</Typography>
|
||||
) : (
|
||||
<TableContainer component={Paper}>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Typ</TableCell>
|
||||
<TableCell>Trigger</TableCell>
|
||||
<TableCell>Zielraum</TableCell>
|
||||
<TableCell>Abonnierbar</TableCell>
|
||||
<TableCell>Aktiv</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rules.map((rule) => (
|
||||
<TableRow
|
||||
key={rule.id}
|
||||
hover
|
||||
sx={{ cursor: 'pointer' }}
|
||||
onClick={() => navigate(`/geplante-nachrichten/${rule.id}`)}
|
||||
>
|
||||
<TableCell>{rule.name}</TableCell>
|
||||
<TableCell>
|
||||
<Chip
|
||||
label={MESSAGE_TYPE_LABELS[rule.message_type] ?? rule.message_type}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>{triggerSummary(rule)}</TableCell>
|
||||
<TableCell>{rule.target_room_name ?? rule.target_room_token}</TableCell>
|
||||
<TableCell>{rule.subscribable ? 'Ja' : 'Nein'}</TableCell>
|
||||
<TableCell>
|
||||
<Switch
|
||||
checked={rule.active}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onChange={() => handleToggleActive(rule)}
|
||||
size="small"
|
||||
disabled={!canEdit}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box sx={{ display: 'flex', gap: 0.5 }} onClick={(e) => e.stopPropagation()}>
|
||||
{canEdit && (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => navigate(`/geplante-nachrichten/${rule.id}/bearbeiten`)}
|
||||
>
|
||||
<EditIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
{canEdit && (
|
||||
deleteConfirmId === rule.id ? (
|
||||
<Button
|
||||
size="small"
|
||||
color="error"
|
||||
variant="contained"
|
||||
onClick={() => handleDelete(rule.id)}
|
||||
>
|
||||
Löschen?
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={() => setDeleteConfirmId(rule.id)}
|
||||
>
|
||||
Löschen
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
{tab === 1 && canEdit && <EinzelNachrichtenTab />}
|
||||
</Box>
|
||||
</DashboardLayout>
|
||||
);
|
||||
|
||||
@@ -75,8 +75,13 @@ export default function GeplanteMachrichtenDetail() {
|
||||
|
||||
const handleSubscribe = async () => {
|
||||
if (!id) return;
|
||||
await scheduledMessagesApi.subscribe(id, '');
|
||||
queryClient.invalidateQueries({ queryKey: ['scheduled-messages', id] });
|
||||
try {
|
||||
const roomResult = await scheduledMessagesApi.getMyBotRoom();
|
||||
await scheduledMessagesApi.subscribe(id, roomResult.data.room_token);
|
||||
queryClient.invalidateQueries({ queryKey: ['scheduled-messages', id] });
|
||||
} catch {
|
||||
showError('Abonnieren fehlgeschlagen. Bitte Nextcloud-Konto in den Einstellungen verbinden.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnsubscribe = async () => {
|
||||
|
||||
@@ -81,6 +81,7 @@ const ORDERABLE_NAV_ITEMS = [
|
||||
{ text: 'Checklisten', path: '/checklisten', permission: 'checklisten:view' },
|
||||
{ text: 'Buchhaltung', path: '/buchhaltung', permission: 'buchhaltung:view' },
|
||||
{ text: 'Issues', path: '/issues', permission: 'issues:view_own' },
|
||||
{ text: 'Geplante Nachrichten', path: '/geplante-nachrichten', permission: 'scheduled_messages:view' },
|
||||
];
|
||||
|
||||
function SortableNavItem({ id, text }: { id: string; text: string }) {
|
||||
@@ -130,7 +131,12 @@ function SubscriptionsCard() {
|
||||
if (isSubscribed) {
|
||||
await scheduledMessagesApi.unsubscribe(ruleId);
|
||||
} else {
|
||||
await scheduledMessagesApi.subscribe(ruleId, '');
|
||||
try {
|
||||
const roomResult = await scheduledMessagesApi.getMyBotRoom();
|
||||
await scheduledMessagesApi.subscribe(ruleId, roomResult.data.room_token);
|
||||
} catch {
|
||||
return; // silently skip — user will see no change in toggle
|
||||
}
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: ['scheduled-messages'] });
|
||||
};
|
||||
|
||||
@@ -9,34 +9,37 @@ import type {
|
||||
|
||||
export const scheduledMessagesApi = {
|
||||
getAll: () =>
|
||||
api.get<ScheduledMessagesListResponse>('/scheduled-messages').then(r => r.data),
|
||||
api.get<ScheduledMessagesListResponse>('/api/scheduled-messages').then(r => r.data),
|
||||
|
||||
getById: (id: string) =>
|
||||
api.get<ScheduledMessageDetailResponse>(`/scheduled-messages/${id}`).then(r => r.data),
|
||||
api.get<ScheduledMessageDetailResponse>(`/api/scheduled-messages/${id}`).then(r => r.data),
|
||||
|
||||
getRooms: () =>
|
||||
api.get<RoomsResponse>('/scheduled-messages/rooms').then(r => r.data),
|
||||
api.get<RoomsResponse>('/api/scheduled-messages/rooms').then(r => r.data),
|
||||
|
||||
create: (data: Partial<ScheduledMessageRule>) =>
|
||||
api.post<ScheduledMessageDetailResponse>('/scheduled-messages', data).then(r => r.data),
|
||||
api.post<ScheduledMessageDetailResponse>('/api/scheduled-messages', data).then(r => r.data),
|
||||
|
||||
update: (id: string, data: Partial<ScheduledMessageRule>) =>
|
||||
api.patch<ScheduledMessageDetailResponse>(`/scheduled-messages/${id}`, data).then(r => r.data),
|
||||
api.patch<ScheduledMessageDetailResponse>(`/api/scheduled-messages/${id}`, data).then(r => r.data),
|
||||
|
||||
delete: (id: string) =>
|
||||
api.delete(`/scheduled-messages/${id}`).then(r => r.data),
|
||||
api.delete(`/api/scheduled-messages/${id}`).then(r => r.data),
|
||||
|
||||
subscribe: (id: string, roomToken: string) =>
|
||||
api.post(`/scheduled-messages/${id}/subscribe`, { room_token: roomToken }).then(r => r.data),
|
||||
api.post(`/api/scheduled-messages/${id}/subscribe`, { room_token: roomToken }).then(r => r.data),
|
||||
|
||||
unsubscribe: (id: string) =>
|
||||
api.delete(`/scheduled-messages/${id}/subscribe`).then(r => r.data),
|
||||
api.delete(`/api/scheduled-messages/${id}/subscribe`).then(r => r.data),
|
||||
|
||||
trigger: (id: string) =>
|
||||
api.post(`/scheduled-messages/${id}/trigger`).then(r => r.data),
|
||||
api.post(`/api/scheduled-messages/${id}/trigger`).then(r => r.data),
|
||||
|
||||
getMyBotRoom: () =>
|
||||
api.get<{ data: { room_token: string } }>('/api/scheduled-messages/my-bot-room').then(r => r.data),
|
||||
|
||||
getOneTimeMessages: () =>
|
||||
api.get<{ data: OneTimeMessage[] }>('/scheduled-messages/one-time').then(r => r.data),
|
||||
api.get<{ data: OneTimeMessage[] }>('/api/scheduled-messages/one-time').then(r => r.data),
|
||||
|
||||
createOneTimeMessage: (data: {
|
||||
message: string;
|
||||
@@ -44,8 +47,8 @@ export const scheduledMessagesApi = {
|
||||
target_room_name?: string;
|
||||
send_at: string;
|
||||
}) =>
|
||||
api.post<{ data: OneTimeMessage }>('/scheduled-messages/one-time', data).then(r => r.data),
|
||||
api.post<{ data: OneTimeMessage }>('/api/scheduled-messages/one-time', data).then(r => r.data),
|
||||
|
||||
deleteOneTimeMessage: (id: string) =>
|
||||
api.delete(`/scheduled-messages/one-time/${id}`).then(r => r.data),
|
||||
api.delete(`/api/scheduled-messages/one-time/${id}`).then(r => r.data),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user