layout improvements
This commit is contained in:
parent
f92219f61f
commit
f76999ed2e
@ -39,108 +39,130 @@ const { authenticated, userEmail, userId } = authResult;
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Contribution Options -->
|
<!-- Contribution Options -->
|
||||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 2rem; margin-bottom: 3rem;">
|
<!-- WRAPPER -->
|
||||||
|
<div
|
||||||
<!-- Tools, Methods & Concepts -->
|
style="
|
||||||
<div class="card" style="padding: 2rem; border-left: 4px solid var(--color-primary); cursor: pointer; transition: var(--transition-fast);" onclick="window.location.href='/contribute/tool'">
|
display:grid;
|
||||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||||
<div style="width: 48px; height: 48px; background-color: var(--color-primary); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
gap:2rem;
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
align-items:stretch;
|
||||||
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
margin-bottom: 2rem;
|
||||||
</svg>
|
"
|
||||||
</div>
|
>
|
||||||
<h3 style="margin: 0; color: var(--color-primary); font-size: 1.25rem;">Tools, Methods & Concepts</h3>
|
|
||||||
</div>
|
<!-- Tools, Methods & Concepts -->
|
||||||
|
<div class="card"
|
||||||
<p style="margin-bottom: 1.5rem; line-height: 1.6;">
|
style="padding: 2rem; border-left: 4px solid var(--color-primary); cursor: pointer; transition: var(--transition-fast);
|
||||||
Add new software tools, forensic methodologies, or fundamental concepts to our database.
|
display:flex; flex-direction:column;"
|
||||||
Includes detailed forms for metadata, licensing, platforms, and categorization.
|
onclick="window.location.href='/contribute/tool'">
|
||||||
|
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||||
|
<div style="width: 48px; height: 48px; background-color: var(--color-primary); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
||||||
|
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="margin: 0; color: var(--color-primary); font-size: 1.25rem;">Tools, Methods & Concepts</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="margin-bottom: 1.5rem; line-height: 1.6;">
|
||||||
|
Add new software tools, forensic methodologies, or fundamental concepts to our database.
|
||||||
|
Includes detailed forms for metadata, licensing, platforms, and categorization.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem;">
|
||||||
|
<span class="badge" style="background-color: var(--color-primary); color: white;">Software Tools</span>
|
||||||
|
<span class="badge" style="background-color: var(--color-method); color: white;">Methods</span>
|
||||||
|
<span class="badge" style="background-color: var(--color-concept); color: white;">Concepts</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top:auto; display:flex; gap:1rem;">
|
||||||
|
<a href="/contribute/tool" class="btn btn-primary" style="flex: 1;">Add New Entry</a>
|
||||||
|
<a href="/contribute/tool?mode=browse" class="btn btn-secondary" style="flex: 1;">Edit Existing</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Knowledgebase Articles -->
|
||||||
|
<div class="card"
|
||||||
|
style="padding: 2rem; border-left: 4px solid var(--color-accent); cursor: pointer; transition: var(--transition-fast);
|
||||||
|
display:flex; flex-direction:column;"
|
||||||
|
onclick="window.location.href='/contribute/knowledgebase'">
|
||||||
|
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||||
|
<div style="width: 48px; height: 48px; background-color: var(--color-accent); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||||
|
<polyline points="14 2 14 8 20 8"/>
|
||||||
|
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||||
|
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||||
|
<polyline points="10 9 9 9 8 9"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="margin: 0; color: var(--color-accent); font-size: 1.25rem;">Knowledgebase Articles</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="margin-bottom: 1.5rem; line-height: 1.6;">
|
||||||
|
Write detailed guides, tutorials, configuration instructions, and best practices.
|
||||||
|
Features a markdown editor with live preview and media upload capabilities.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem;">
|
||||||
|
<span class="badge badge-secondary">Installation Guides</span>
|
||||||
|
<span class="badge badge-secondary">Tutorials</span>
|
||||||
|
<span class="badge badge-secondary">Best Practices</span>
|
||||||
|
<span class="badge badge-secondary">Case Studies</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top:auto; display:flex; gap:1rem;">
|
||||||
|
<a href="/contribute/knowledgebase" class="btn btn-accent" style="flex: 1;">Write Article</a>
|
||||||
|
<a href="/knowledgebase" class="btn btn-secondary" style="flex: 1;">View Articles</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Issues & Improvements -->
|
||||||
|
<div class="card"
|
||||||
|
style="padding: 2rem; border-left: 4px solid var(--color-accent); cursor: pointer; transition: var(--transition-fast);
|
||||||
|
display:flex; flex-direction:column;">
|
||||||
|
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
||||||
|
<div style="width: 48px; height: 48px; background-color: var(--color-warning); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="10"/>
|
||||||
|
<line x1="12" y1="8" x2="12" y2="12"/>
|
||||||
|
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="margin: 0; color: var(--color-warning); font-size: 1.25rem;">Issues & Improvements</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: 2fr 1fr; gap: 2rem; align-items: center;">
|
||||||
|
<div style="display:flex; flex-direction:column;">
|
||||||
|
<p style="margin-bottom: 1rem; line-height: 1.6;">
|
||||||
|
Found incorrect information, broken links, or have suggestions for improvements?
|
||||||
|
Report issues directly in our Git repository or suggest enhancements to existing entries.
|
||||||
</p>
|
</p>
|
||||||
|
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
|
||||||
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem;">
|
<span class="badge" style="background-color: var(--color-warning); color: white;">Bug Reports</span>
|
||||||
<span class="badge" style="background-color: var(--color-primary); color: white;">Software Tools</span>
|
<span class="badge" style="background-color: var(--color-warning); color: white;">Corrections</span>
|
||||||
<span class="badge" style="background-color: var(--color-method); color: white;">Methods</span>
|
<span class="badge" style="background-color: var(--color-warning); color: white;">Suggestions</span>
|
||||||
<span class="badge" style="background-color: var(--color-concept); color: white;">Concepts</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; gap: 1rem;">
|
|
||||||
<a href="/contribute/tool" class="btn btn-primary" style="flex: 1;">Add New Entry</a>
|
|
||||||
<a href="/contribute/tool?mode=browse" class="btn btn-secondary" style="flex: 1;">Edit Existing</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||||
<!-- Knowledgebase Articles -->
|
<a href="https://git.cc24.dev/mstoeck3/cc24-hub/issues/new" target="_blank" rel="noopener noreferrer" class="btn" style="background-color: var(--color-warning); color: white; border-color: var(--color-warning);">
|
||||||
<div class="card" style="padding: 2rem; border-left: 4px solid var(--color-accent); cursor: pointer; transition: var(--transition-fast);" onclick="window.location.href='/contribute/knowledgebase'">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
||||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||||
<div style="width: 48px; height: 48px; background-color: var(--color-accent); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
<polyline points="15 3 21 3 21 9"/>
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
<line x1="10" y1="14" x2="21" y2="3"/>
|
||||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
</svg>
|
||||||
<polyline points="14 2 14 8 20 8"/>
|
Report Issue
|
||||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
</a>
|
||||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
|
||||||
<polyline points="10 9 9 9 8 9"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h3 style="margin: 0; color: var(--color-accent); font-size: 1.25rem;">Knowledgebase Articles</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p style="margin-bottom: 1.5rem; line-height: 1.6;">
|
|
||||||
Write detailed guides, tutorials, configuration instructions, and best practices.
|
|
||||||
Features a markdown editor with live preview and media upload capabilities.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem;">
|
|
||||||
<span class="badge badge-secondary">Installation Guides</span>
|
|
||||||
<span class="badge badge-secondary">Tutorials</span>
|
|
||||||
<span class="badge badge-secondary">Best Practices</span>
|
|
||||||
<span class="badge badge-secondary">Case Studies</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; gap: 1rem;">
|
|
||||||
<a href="/contribute/knowledgebase" class="btn btn-accent" style="flex: 1;">Write Article</a>
|
|
||||||
<a href="/knowledgebase" class="btn btn-secondary" style="flex: 1;">View Articles</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Issues & Improvements -->
|
|
||||||
<div class="card" style="padding: 2rem; border-left: 4px solid var(--color-warning); grid-column: 1 / -1;">
|
|
||||||
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
|
|
||||||
<div style="width: 48px; height: 48px; background-color: var(--color-warning); border-radius: 0.5rem; display: flex; align-items: center; justify-content: center;">
|
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
|
||||||
<circle cx="12" cy="12" r="10"/>
|
|
||||||
<line x1="12" y1="8" x2="12" y2="12"/>
|
|
||||||
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h3 style="margin: 0; color: var(--color-warning); font-size: 1.25rem;">Issues & Improvements</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: grid; grid-template-columns: 2fr 1fr; gap: 2rem; align-items: center;">
|
|
||||||
<div>
|
|
||||||
<p style="margin-bottom: 1rem; line-height: 1.6;">
|
|
||||||
Found incorrect information, broken links, or have suggestions for improvements?
|
|
||||||
Report issues directly in our Git repository or suggest enhancements to existing entries.
|
|
||||||
</p>
|
|
||||||
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
|
|
||||||
<span class="badge" style="background-color: var(--color-warning); color: white;">Bug Reports</span>
|
|
||||||
<span class="badge" style="background-color: var(--color-warning); color: white;">Corrections</span>
|
|
||||||
<span class="badge" style="background-color: var(--color-warning); color: white;">Suggestions</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
|
||||||
<a href="https://git.cc24.dev/mstoeck3/cc24-hub/issues/new" target="_blank" rel="noopener noreferrer" class="btn" style="background-color: var(--color-warning); color: white; border-color: var(--color-warning);">
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem;">
|
|
||||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
|
||||||
<polyline points="15 3 21 3 21 9"/>
|
|
||||||
<line x1="10" y1="14" x2="21" y2="3"/>
|
|
||||||
</svg>
|
|
||||||
Report Issue
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Push this actions block down if you add more later -->
|
||||||
|
<div style="margin-top:auto;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Guidelines -->
|
<!-- Guidelines -->
|
||||||
<div class="card" style="margin-bottom: 2rem;">
|
<div class="card" style="margin-bottom: 2rem;">
|
||||||
<h3 style="margin-bottom: 1.5rem; color: var(--color-text);">Contribution Guidelines</h3>
|
<h3 style="margin-bottom: 1.5rem; color: var(--color-text);">Contribution Guidelines</h3>
|
||||||
|
157
src/utils/api.ts
Normal file
157
src/utils/api.ts
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
// src/utils/api.ts
|
||||||
|
|
||||||
|
// Standard JSON headers for all API responses
|
||||||
|
const JSON_HEADERS = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base function to create consistent API responses
|
||||||
|
* All other response helpers use this internally
|
||||||
|
*/
|
||||||
|
export function createAPIResponse(data: any, status: number = 200, additionalHeaders?: Record<string, string>): Response {
|
||||||
|
const headers = additionalHeaders
|
||||||
|
? { ...JSON_HEADERS, ...additionalHeaders }
|
||||||
|
: JSON_HEADERS;
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(data), {
|
||||||
|
status,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success responses (2xx status codes)
|
||||||
|
*/
|
||||||
|
export const apiResponse = {
|
||||||
|
// 200 - Success with data
|
||||||
|
success: (data: any = { success: true }): Response =>
|
||||||
|
createAPIResponse(data, 200),
|
||||||
|
|
||||||
|
// 201 - Created (for contribution submissions, uploads, etc.)
|
||||||
|
created: (data: any = { success: true }): Response =>
|
||||||
|
createAPIResponse(data, 201),
|
||||||
|
|
||||||
|
// 202 - Accepted (for async operations)
|
||||||
|
accepted: (data: any = { success: true, message: 'Request accepted for processing' }): Response =>
|
||||||
|
createAPIResponse(data, 202)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client error responses (4xx status codes)
|
||||||
|
*/
|
||||||
|
export const apiError = {
|
||||||
|
// 400 - Bad Request
|
||||||
|
badRequest: (message: string = 'Bad request', details?: string[]): Response =>
|
||||||
|
createAPIResponse({
|
||||||
|
success: false,
|
||||||
|
error: message,
|
||||||
|
...(details && { details })
|
||||||
|
}, 400),
|
||||||
|
|
||||||
|
// 401 - Unauthorized
|
||||||
|
unauthorized: (message: string = 'Authentication required'): Response =>
|
||||||
|
createAPIResponse({ success: false, error: message }, 401),
|
||||||
|
|
||||||
|
// 403 - Forbidden
|
||||||
|
forbidden: (message: string = 'Access denied'): Response =>
|
||||||
|
createAPIResponse({ success: false, error: message }, 403),
|
||||||
|
|
||||||
|
// 404 - Not Found
|
||||||
|
notFound: (message: string = 'Resource not found'): Response =>
|
||||||
|
createAPIResponse({ success: false, error: message }, 404),
|
||||||
|
|
||||||
|
// 422 - Unprocessable Entity (validation errors)
|
||||||
|
validation: (message: string = 'Validation failed', details?: string[]): Response =>
|
||||||
|
createAPIResponse({
|
||||||
|
success: false,
|
||||||
|
error: message,
|
||||||
|
...(details && { details })
|
||||||
|
}, 422),
|
||||||
|
|
||||||
|
// 429 - Rate Limited
|
||||||
|
rateLimit: (message: string = 'Rate limit exceeded. Please wait before trying again.'): Response =>
|
||||||
|
createAPIResponse({ success: false, error: message }, 429)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server error responses (5xx status codes)
|
||||||
|
*/
|
||||||
|
export const apiServerError = {
|
||||||
|
// 500 - Internal Server Error
|
||||||
|
internal: (message: string = 'Internal server error'): Response =>
|
||||||
|
createAPIResponse({ success: false, error: message }, 500),
|
||||||
|
|
||||||
|
// 502 - Bad Gateway (external service issues)
|
||||||
|
badGateway: (message: string = 'External service error'): Response =>
|
||||||
|
createAPIResponse({ success: false, error: message }, 502),
|
||||||
|
|
||||||
|
// 503 - Service Unavailable
|
||||||
|
unavailable: (message: string = 'Service temporarily unavailable'): Response =>
|
||||||
|
createAPIResponse({ success: false, error: message }, 503),
|
||||||
|
|
||||||
|
// 504 - Gateway Timeout
|
||||||
|
timeout: (message: string = 'Request timeout'): Response =>
|
||||||
|
createAPIResponse({ success: false, error: message }, 504)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized response helpers for common patterns
|
||||||
|
*/
|
||||||
|
export const apiSpecial = {
|
||||||
|
// JSON parsing error
|
||||||
|
invalidJSON: (): Response =>
|
||||||
|
apiError.badRequest('Invalid JSON in request body'),
|
||||||
|
|
||||||
|
// Missing required fields
|
||||||
|
missingRequired: (fields: string[]): Response =>
|
||||||
|
apiError.badRequest(`Missing required fields: ${fields.join(', ')}`),
|
||||||
|
|
||||||
|
// Empty request body
|
||||||
|
emptyBody: (): Response =>
|
||||||
|
apiError.badRequest('Request body cannot be empty'),
|
||||||
|
|
||||||
|
// File upload responses
|
||||||
|
uploadSuccess: (data: { url: string; filename: string; size: number; storage: string }): Response =>
|
||||||
|
apiResponse.created(data),
|
||||||
|
|
||||||
|
uploadFailed: (error: string): Response =>
|
||||||
|
apiServerError.internal(`Upload failed: ${error}`),
|
||||||
|
|
||||||
|
// Contribution responses
|
||||||
|
contributionSuccess: (data: { prUrl?: string; branchName?: string; message: string }): Response =>
|
||||||
|
apiResponse.created({ success: true, ...data }),
|
||||||
|
|
||||||
|
contributionFailed: (error: string): Response =>
|
||||||
|
apiServerError.internal(`Contribution failed: ${error}`)
|
||||||
|
};
|
||||||
|
|
||||||
|
export const apiWithHeaders = {
|
||||||
|
// Success with custom headers (e.g., Set-Cookie)
|
||||||
|
successWithHeaders: (data: any, headers: Record<string, string>): Response =>
|
||||||
|
createAPIResponse(data, 200, headers),
|
||||||
|
|
||||||
|
// Redirect response
|
||||||
|
redirect: (location: string, temporary: boolean = true): Response =>
|
||||||
|
new Response(null, {
|
||||||
|
status: temporary ? 302 : 301,
|
||||||
|
headers: { 'Location': location }
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function handleAPIRequest<T>(
|
||||||
|
operation: () => Promise<T>,
|
||||||
|
errorMessage: string = 'Request processing failed'
|
||||||
|
): Promise<T | Response> {
|
||||||
|
try {
|
||||||
|
return await operation();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`API Error: ${errorMessage}:`, error);
|
||||||
|
return apiServerError.internal(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createAuthErrorResponse = apiError.unauthorized;
|
||||||
|
export const createBadRequestResponse = apiError.badRequest;
|
||||||
|
export const createSuccessResponse = apiResponse.success;
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user