init
This commit is contained in:
@@ -30,7 +30,6 @@ The goal of this task is to add an "Add Server" button and creation functionalit
|
||||
- Cancel and Create buttons with loading state handling.
|
||||
- **Store Integration:** `useServerStore` must provide a `createServer` action that performs `POST /servers` with the appropriate payload (`{ name, password, is_default }`).
|
||||
|
||||
|
||||
# Technical Design
|
||||
|
||||
### Current Implementation
|
||||
@@ -80,7 +79,6 @@ graph LR
|
||||
ServerStore -->|Updates servers list| AppLayout
|
||||
```
|
||||
|
||||
|
||||
# Testing
|
||||
|
||||
### Validation Approach
|
||||
@@ -97,10 +95,9 @@ graph LR
|
||||
- **Validation Error:** Empty server name disables the create button or shows an alert.
|
||||
- **API Error Handling:** Catch and log API failures gracefully.
|
||||
|
||||
|
||||
# Delivery Steps
|
||||
|
||||
### Step 1: Extend server Pinia store with createServer action
|
||||
### * Step 1: Extend server Pinia store with createServer action
|
||||
Extend the server Pinia store with createServer action.
|
||||
- Update `frontend/src/stores/server.ts` to include `createServer(payload: { name: string; password?: string | null; is_default?: boolean })`.
|
||||
- Handle error states, API POST requests to `/servers`, and state updates to push the newly created server to `this.servers`.
|
||||
|
||||
@@ -5,66 +5,36 @@ sessionId: session-260727-090636-1x6i
|
||||
# Requirements
|
||||
|
||||
### Overview & Goals
|
||||
The goal of this task is to adapt the frontend data-loading policy for channels and categories. Previously, all channels and categories were fetched globally once upon initial load. Now, whenever the user swaps servers (or enters a server view), channels and categories should be reloaded specifically for that active server using the backend query parameters (`server_id`) implemented in the previous backend task.
|
||||
The goal of this task is to add an "Add Server" button and creation modal on the frontend (leveraging existing backend server creation endpoints), and investigate/address any server scoping issues with channels and categories when multiple servers exist.
|
||||
|
||||
### Scope
|
||||
- **In Scope**:
|
||||
- Updating `useChannelStore` and `useCategoryStore` in Pinia to accept a `serverId` query parameter when fetching channels and categories.
|
||||
- Updating `src/pages/server/index.vue` (and/or router navigation / lifecycle hooks) to trigger fetching of channels and categories filtered by the current `serverId` whenever the active server changes.
|
||||
- Resetting or clearing channel and category state appropriately when switching servers.
|
||||
- Adding an "Add Server" button in the frontend layout/navigation (e.g. in `AppLayout.vue` or sidebar) with a creation dialog supporting server name, password, and default flags.
|
||||
- Verifying and ensuring that channel and category queries correctly filter and isolate data per server.
|
||||
- **Out of Scope**:
|
||||
- Backend changes (already completed in previous task).
|
||||
- Changes to other entities (servers, messages, etc.) unless directly related to channel/category scoping.
|
||||
- Major backend restructuring (existing CRUD and filter endpoints are already fully implemented).
|
||||
|
||||
### User Stories
|
||||
- **As a user**, when I switch from one server to another, I want the channels and categories list to be reloaded automatically for the newly selected server so that I only see relevant content.
|
||||
- **As a user**, I want an "Add Server" button in the UI so that I can easily create a new server.
|
||||
- **As a user**, when I switch between servers, I want to ensure that channels and categories are strictly scoped to the active server.
|
||||
|
||||
### Functional Requirements
|
||||
1. **Store Actions (`fetchChannels`, `fetchCategories`)**:
|
||||
- Both actions must accept an optional `serverId?: string`.
|
||||
- When `serverId` is provided, requests must be made to `/channels?server_id=...` and `/categories?server_id=...`.
|
||||
2. **Server View Component (`/src/pages/server/index.vue`)**:
|
||||
- On component mount or when `serverId` route parameter changes (e.g. via `watch(() => route.params.serverId, ...)`), trigger fetching of channels and categories for the new `serverId`.
|
||||
- Clear existing channels/categories or show loading states during the fetch transition.
|
||||
1. **Add Server UI (`AppLayout.vue`)**:
|
||||
- Provide a prominent button in the sidebar to open the "Create Server" dialog.
|
||||
- Collect server details (`name`, optional `password`, `is_default`) and call `serverStore.createServer`.
|
||||
- On successful creation, navigate to the new server's view (`/server/${newServer.id}`).
|
||||
2. **Server Scoping Verification**:
|
||||
- Ensure channels and categories fetched for a server correctly correspond to that server ID.
|
||||
|
||||
# Technical Design
|
||||
|
||||
### Current Implementation
|
||||
- Currently, `useSessionStore.loadInitialData()` calls `serverStore.fetchServers()`, `categoryStore.fetchCategories()`, and `channelStore.fetchChannels()` without parameters, retrieving all records from `/channels` and `/categories`.
|
||||
- `src/pages/server/index.vue` displays `channels` from `useChannelStore`, but does not trigger refetching when switching between different servers.
|
||||
|
||||
### Key Decisions
|
||||
- **Query Parameter Usage**: Leverage the backend endpoints `/channels?server_id=<id>` and `/categories?server_id=<id>` that were created previously.
|
||||
- **Store & Lifecycle Integration**: Use Vue router route watchers in `src/pages/server/index.vue` (or a dedicated composable/watcher) to detect `serverId` changes and invoke store fetch actions.
|
||||
- Backend server creation (`POST /servers`) and listing (`GET /servers`) are fully implemented.
|
||||
- Frontend `useServerStore` has `createServer` and `fetchServers`.
|
||||
- `AppLayout.vue` already includes a dialog structure for creating servers triggered by an `mdi-plus` icon in the navigation drawer.
|
||||
|
||||
### Proposed Changes
|
||||
1. **`src/stores/channel.ts`**:
|
||||
- Modify `fetchChannels(serverId?: string)` to build query string `?server_id=${serverId}` if `serverId` is provided.
|
||||
2. **`src/stores/category.ts`**:
|
||||
- Modify `fetchCategories(serverId?: string)` to build query string `?server_id=${serverId}` if `serverId` is provided.
|
||||
3. **`src/pages/server/index.vue`**:
|
||||
- Add a watcher on `route.params.serverId` (or `serverId` prop).
|
||||
- On mount and when `serverId` changes, call `channelStore.fetchChannels(serverId)` and `categoryStore.fetchCategories(serverId)`.
|
||||
|
||||
### File Structure
|
||||
- **Modified Files**:
|
||||
- `frontend/src/stores/channel.ts`
|
||||
- `frontend/src/stores/category.ts`
|
||||
- `frontend/src/pages/server/index.vue`
|
||||
|
||||
### Risks & Mitigations
|
||||
- **Race conditions during rapid server switching**: Mitigated by handling loading states or canceling/ignoring stale promises if necessary, or simply allowing the latest fetch response to overwrite the store state.
|
||||
|
||||
# Delivery Steps
|
||||
|
||||
### ✓ Step 1: Update channel and category stores to support server-scoped fetching
|
||||
Update Pinia stores for channels and categories to support filtered fetching by server_id.
|
||||
- Update `useChannelStore.fetchChannels(serverId?: string)` to accept an optional `serverId` and pass it as a query parameter (`/channels?server_id=...`).
|
||||
- Update `useCategoryStore.fetchCategories(serverId?: string)` to accept an optional `serverId` and pass it as a query parameter (`/categories?server_id=...`).
|
||||
- Clear channels and categories when no server is selected or when swapping servers.
|
||||
|
||||
### ✓ Step 2: Integrate server-scoped data loading into server view and router navigation
|
||||
Integrate server-scoped loading into the server view lifecycle and route changes.
|
||||
- Update `/src/pages/server/index.vue` to watch `serverId` (or trigger on route parameter change / component mount) and fetch channels and categories specifically for the active `serverId`.
|
||||
- Clear channels and categories when switching between servers or leaving the server view.
|
||||
- Ensure proper loading states and error handling during server switching.
|
||||
1. **`frontend/src/layouts/AppLayout.vue`**:
|
||||
- Ensure the server creation dialog and form bindings are fully wired up and intuitive.
|
||||
2. **Review Channel & Category Scoping**:
|
||||
- Verify that `useChannelStore.fetchChannels(serverId)` and `useCategoryStore.fetchCategories(serverId)` correctly pass `server_id` and that the backend filters properly.
|
||||
@@ -0,0 +1,130 @@
|
||||
<script lang="ts" setup>
|
||||
import {ref, watch} from 'vue'
|
||||
import {useChannelStore} from '@/stores/channel'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
serverId: string
|
||||
categoryId?: string | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'created', channel: any): void
|
||||
}>()
|
||||
|
||||
const channelStore = useChannelStore()
|
||||
|
||||
const formData = ref({
|
||||
name: '',
|
||||
channel_type: 'text',
|
||||
position: 0
|
||||
})
|
||||
const isSubmitting = ref(false)
|
||||
|
||||
const channelTypeOptions = [
|
||||
{title: 'Text', value: 'text'},
|
||||
{title: 'Voice', value: 'voice'},
|
||||
{title: 'DM', value: 'dm'}
|
||||
]
|
||||
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
name: '',
|
||||
channel_type: 'text',
|
||||
position: 0
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
emit('update:modelValue', false)
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.value.name.trim()) {
|
||||
alert('Channel name is required')
|
||||
return
|
||||
}
|
||||
|
||||
isSubmitting.value = true
|
||||
try {
|
||||
const newChannel = await channelStore.createChannel({
|
||||
name: formData.value.name,
|
||||
channel_type: formData.value.channel_type,
|
||||
server_id: props.serverId,
|
||||
category_id: props.categoryId ?? null,
|
||||
position: formData.value.position
|
||||
})
|
||||
emit('created', newChannel)
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
console.error('Failed to create channel:', error)
|
||||
} finally {
|
||||
isSubmitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(isOpen) => {
|
||||
if (!isOpen) {
|
||||
resetForm()
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog :model-value="modelValue" width="400" @update:model-value="handleClose">
|
||||
<v-card>
|
||||
<v-card-title>Create Channel</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="mt-4 space-y-4">
|
||||
<v-text-field
|
||||
v-model="formData.name"
|
||||
density="compact"
|
||||
label="Channel Name"
|
||||
outlined
|
||||
@keyup.enter="handleSubmit"
|
||||
></v-text-field>
|
||||
|
||||
<v-select
|
||||
v-model="formData.channel_type"
|
||||
:items="channelTypeOptions"
|
||||
density="compact"
|
||||
label="Channel Type"
|
||||
outlined
|
||||
></v-select>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:disabled="isSubmitting"
|
||||
variant="text"
|
||||
@click="handleClose"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
:disabled="!formData.name.trim()"
|
||||
:loading="isSubmitting"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
Create
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.space-y-4 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,57 @@
|
||||
<script lang="ts" setup>
|
||||
import {storeToRefs} from 'pinia'
|
||||
import {useServerStore} from '@/stores/server'
|
||||
import {ref} from 'vue'
|
||||
import {useRouter} from 'vue-router'
|
||||
|
||||
const serverStore = useServerStore()
|
||||
const router = useRouter()
|
||||
|
||||
const {servers} = storeToRefs(serverStore)
|
||||
console.log(servers.value)
|
||||
|
||||
const showDialog = ref(false)
|
||||
const formData = ref({
|
||||
name: '',
|
||||
password: '',
|
||||
is_default: false
|
||||
})
|
||||
const isSubmitting = ref(false)
|
||||
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
name: '',
|
||||
password: '',
|
||||
is_default: false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.value.name.trim()) {
|
||||
alert('Server name is required');
|
||||
return;
|
||||
}
|
||||
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
const newServer = await serverStore.createServer({
|
||||
name: formData.value.name,
|
||||
password: formData.value.password || null,
|
||||
is_default: formData.value.is_default
|
||||
});
|
||||
showDialog.value = false;
|
||||
resetForm();
|
||||
router.push(`/server/${newServer.id}`);
|
||||
} catch (error) {
|
||||
console.error('Failed to create server:', error);
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
showDialog.value = false;
|
||||
resetForm();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -49,12 +95,70 @@ console.log(servers.value)
|
||||
></v-avatar>
|
||||
</router-link>
|
||||
|
||||
<v-btn
|
||||
class="d-block mx-auto mb-9"
|
||||
color="grey-lighten-2"
|
||||
icon="mdi-plus"
|
||||
size="28"
|
||||
variant="flat"
|
||||
@click="showDialog = true"
|
||||
></v-btn>
|
||||
|
||||
</v-navigation-drawer>
|
||||
<router-view/>
|
||||
|
||||
<v-dialog v-model="showDialog" width="400">
|
||||
<v-card>
|
||||
<v-card-title>Create Server</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="mt-4 space-y-4">
|
||||
<v-text-field
|
||||
v-model="formData.name"
|
||||
density="compact"
|
||||
label="Server Name"
|
||||
outlined
|
||||
@keyup.enter="handleSubmit"
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field
|
||||
v-model="formData.password"
|
||||
density="compact"
|
||||
label="Password (optional)"
|
||||
outlined
|
||||
type="password"
|
||||
@keyup.enter="handleSubmit"
|
||||
></v-text-field>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:disabled="isSubmitting"
|
||||
variant="text"
|
||||
@click="handleCancel"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
:disabled="!formData.name.trim()"
|
||||
:loading="isSubmitting"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
Create
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.space-y-4 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -2,13 +2,14 @@
|
||||
import {storeToRefs} from 'pinia'
|
||||
import {useChannelStore} from '@/stores/channel'
|
||||
import {useCategoryStore} from '@/stores/category'
|
||||
import {ref, watch, onMounted} from 'vue'
|
||||
import {ref, watch} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
import CreateChannelDialog from '@/components/channel/CreateChannelDialog.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
serverId: string
|
||||
channelId?: string
|
||||
}>();
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const channelStore = useChannelStore()
|
||||
@@ -41,59 +42,6 @@ watch(
|
||||
)
|
||||
|
||||
const showDialog = ref(false)
|
||||
const formData = ref({
|
||||
name: '',
|
||||
channel_type: 'text',
|
||||
server_id: null,
|
||||
category_id: null,
|
||||
position: 0
|
||||
})
|
||||
const isSubmitting = ref(false)
|
||||
|
||||
const channelTypeOptions = [
|
||||
{title: 'Text', value: 'text'},
|
||||
{title: 'Voice', value: 'voice'},
|
||||
{title: 'DM', value: 'dm'}
|
||||
]
|
||||
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
name: '',
|
||||
channel_type: 'text',
|
||||
server_id: null,
|
||||
category_id: null,
|
||||
position: 0
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formData.value.name.trim()) {
|
||||
alert('Channel name is required');
|
||||
return;
|
||||
}
|
||||
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
await channelStore.createChannel({
|
||||
name: formData.value.name,
|
||||
channel_type: formData.value.channel_type,
|
||||
server_id: formData.value.server_id,
|
||||
category_id: formData.value.category_id,
|
||||
position: formData.value.position
|
||||
});
|
||||
showDialog.value = false;
|
||||
resetForm();
|
||||
} catch (error) {
|
||||
console.error('Failed to create channel:', error);
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
showDialog.value = false;
|
||||
resetForm();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -125,59 +73,12 @@ const handleCancel = () => {
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-dialog v-model="showDialog" width="400">
|
||||
<v-card>
|
||||
<v-card-title>Create Channel</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="mt-4 space-y-4">
|
||||
<v-text-field
|
||||
v-model="formData.name"
|
||||
density="compact"
|
||||
label="Channel Name"
|
||||
outlined
|
||||
@keyup.enter="handleSubmit"
|
||||
></v-text-field>
|
||||
|
||||
<v-select
|
||||
v-model="formData.channel_type"
|
||||
:items="channelTypeOptions"
|
||||
density="compact"
|
||||
label="Channel Type"
|
||||
outlined
|
||||
></v-select>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
:disabled="isSubmitting"
|
||||
variant="text"
|
||||
@click="handleCancel"
|
||||
>
|
||||
Cancel
|
||||
</v-btn>
|
||||
<v-btn
|
||||
:disabled="!formData.name.trim()"
|
||||
:loading="isSubmitting"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
Create
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<CreateChannelDialog
|
||||
v-model="showDialog"
|
||||
:server-id="serverId"
|
||||
/>
|
||||
|
||||
<v-main>
|
||||
<router-view/>
|
||||
</v-main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.space-y-4 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
@@ -3,11 +3,17 @@ import {useApi} from "@/composables/useApi.ts";
|
||||
|
||||
interface Server {
|
||||
id: string
|
||||
name: string
|
||||
is_default: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export const useServerStore = defineStore("server", {
|
||||
state: () => ({
|
||||
servers: [] as Server[]
|
||||
servers: [] as Server[],
|
||||
loading: false,
|
||||
error: null as string | null
|
||||
}),
|
||||
actions: {
|
||||
async fetchServers() {
|
||||
@@ -15,8 +21,30 @@ export const useServerStore = defineStore("server", {
|
||||
const response = await api.get("/servers");
|
||||
this.servers = await response.json();
|
||||
},
|
||||
async createServer(payload: { name: string; password?: string | null; is_default?: boolean }) {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
try {
|
||||
const api = useApi();
|
||||
const response = await api.post("/servers", payload);
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.message || 'Failed to create server');
|
||||
}
|
||||
const newServer = await response.json();
|
||||
this.servers.push(newServer);
|
||||
return newServer;
|
||||
} catch (err) {
|
||||
this.error = err instanceof Error ? err.message : 'Unknown error';
|
||||
throw err;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.servers = [];
|
||||
this.loading = false;
|
||||
this.error = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::models::{channel, channel_role_permission, channel_user_permission};
|
||||
use crate::repositories::types::ChannelFilter;
|
||||
use crate::repositories::{AnyResult, RepositoryContext};
|
||||
use sea_orm::sea_query::OnConflict;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
|
||||
@@ -21,14 +22,11 @@ impl ChannelRepository {
|
||||
Ok(channel::Entity::find().all(&self.context.db).await?)
|
||||
}
|
||||
|
||||
pub async fn filter(&self, server_id: Option<Uuid>, category_id: Option<Uuid>) -> AnyResult<Vec<channel::Model>> {
|
||||
pub async fn filter(&self, filter: ChannelFilter) -> AnyResult<Vec<channel::Model>> {
|
||||
let mut query = channel::Entity::find();
|
||||
if let Some(s_id) = server_id {
|
||||
if let Some(s_id) = filter.server_id {
|
||||
query = query.filter(channel::Column::ServerId.eq(s_id));
|
||||
}
|
||||
if let Some(c_id) = category_id {
|
||||
query = query.filter(channel::Column::CategoryId.eq(c_id));
|
||||
}
|
||||
Ok(query.all(&self.context.db).await?)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,3 +24,7 @@ pub struct MessageFilter {
|
||||
pub before_id: Option<uuid::Uuid>,
|
||||
pub limit: Option<u64>,
|
||||
}
|
||||
|
||||
pub struct ChannelFilter {
|
||||
pub server_id: Option<uuid::Uuid>,
|
||||
}
|
||||
|
||||
@@ -7,15 +7,11 @@ use uuid::Uuid;
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema, utoipa::IntoParams)]
|
||||
pub struct ChannelQueryParams {
|
||||
pub server_id: Option<Uuid>,
|
||||
pub category_id: Option<Uuid>,
|
||||
}
|
||||
|
||||
impl Default for ChannelQueryParams {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
server_id: None,
|
||||
category_id: None,
|
||||
}
|
||||
Self { server_id: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ use crate::core::state::AppState;
|
||||
use crate::http::context::Superuser;
|
||||
use crate::http::error::HTTPError;
|
||||
use crate::routes::channel::dto::{
|
||||
ChannelQueryParams, ChannelResponse, ChannelRolePermissionResponse, ChannelUserPermissionResponse,
|
||||
CreateChannelRequest, SetChannelPermissionRequest, UpdateChannelRequest,
|
||||
ChannelQueryParams, ChannelResponse, ChannelRolePermissionResponse,
|
||||
ChannelUserPermissionResponse, CreateChannelRequest, SetChannelPermissionRequest,
|
||||
UpdateChannelRequest,
|
||||
};
|
||||
use crate::routes::channel::mapper;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, Query, State},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -30,7 +31,8 @@ pub async fn get_all(
|
||||
State(state): State<AppState>,
|
||||
Query(filters): Query<ChannelQueryParams>,
|
||||
) -> Result<Json<Vec<ChannelResponse>>, HTTPError> {
|
||||
let channels = state.repositories.channel.filter(filters.server_id, filters.category_id).await?;
|
||||
let params = mapper::query_params_to_channel_filter(filters);
|
||||
let channels = state.repositories.channel.filter(params).await?;
|
||||
Ok(Json(
|
||||
channels
|
||||
.into_iter()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::models::{channel, channel_role_permission, channel_user_permission};
|
||||
use crate::repositories::types::ChannelFilter;
|
||||
use crate::routes::channel::dto::{
|
||||
ChannelResponse, ChannelRolePermissionResponse, ChannelUserPermissionResponse,
|
||||
CreateChannelRequest, UpdateChannelRequest,
|
||||
ChannelQueryParams, ChannelResponse, ChannelRolePermissionResponse,
|
||||
ChannelUserPermissionResponse, CreateChannelRequest, UpdateChannelRequest,
|
||||
};
|
||||
use sea_orm::Set;
|
||||
use uuid::Uuid;
|
||||
@@ -61,3 +62,9 @@ pub fn channel_role_permission_to_response(
|
||||
permissions: model.permissions as u64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_params_to_channel_filter(params: ChannelQueryParams) -> ChannelFilter {
|
||||
ChannelFilter {
|
||||
server_id: params.server_id,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user