From 00626152c2cd59e4ab03c3c34487611cf819fb1a Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Wed, 3 Jun 2026 13:32:56 +0300 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- svc/repository/activitylog_repository.go | 24 ++-- svc/routes/activitylogs.go | 6 +- ui/.quasar/prod-spa/app.js | 75 ++++++++++ ui/.quasar/prod-spa/client-entry.js | 158 +++++++++++++++++++++ ui/.quasar/prod-spa/client-prefetch.js | 116 +++++++++++++++ ui/.quasar/prod-spa/quasar-user-options.js | 23 +++ ui/src/pages/ActivityLogs.vue | 3 + ui/src/stores/activityLogStore.js | 53 +++---- 8 files changed, 404 insertions(+), 54 deletions(-) create mode 100644 ui/.quasar/prod-spa/app.js create mode 100644 ui/.quasar/prod-spa/client-entry.js create mode 100644 ui/.quasar/prod-spa/client-prefetch.js create mode 100644 ui/.quasar/prod-spa/quasar-user-options.js diff --git a/svc/repository/activitylog_repository.go b/svc/repository/activitylog_repository.go index 73f2365..e883f29 100644 --- a/svc/repository/activitylog_repository.go +++ b/svc/repository/activitylog_repository.go @@ -79,11 +79,11 @@ func ListActivityLogs( q.Page = 1 } // limit <=0 → unlimited - if q.Limit < 0 { - q.Limit = 50 + if q.Limit <= 0 { + q.Limit = 1000 } - if q.Limit > 200 { - q.Limit = 200 + if q.Limit > 1000 { + q.Limit = 1000 } offset := 0 @@ -200,17 +200,13 @@ LEFT JOIN dfrole r ON r.id = ru.dfrole_id %s ORDER BY l.created_at DESC `, whereSQL) - // LIMIT sadece istenirse - if q.Limit > 0 { + listSQL += fmt.Sprintf( + " LIMIT $%d OFFSET $%d", + len(listArgs)+1, + len(listArgs)+2, + ) - listSQL += fmt.Sprintf( - " LIMIT $%d OFFSET $%d", - len(listArgs)+1, - len(listArgs)+2, - ) - - listArgs = append(listArgs, q.Limit, offset) - } + listArgs = append(listArgs, q.Limit, offset) rows, err := db.QueryContext(ctx, listSQL, listArgs...) if err != nil { diff --git a/svc/routes/activitylogs.go b/svc/routes/activitylogs.go index df966e6..b29d21e 100644 --- a/svc/routes/activitylogs.go +++ b/svc/routes/activitylogs.go @@ -36,13 +36,11 @@ func AdminActivityLogsHandler(pgDB *sql.DB) http.HandlerFunc { } } + q.Limit = 1000 if v := r.URL.Query().Get("limit"); v != "" { - if i, err := strconv.Atoi(v); err == nil { + if i, err := strconv.Atoi(v); err == nil && i > 0 { q.Limit = i } - } else { - // limit yoksa unlimited - q.Limit = 0 } // filters diff --git a/ui/.quasar/prod-spa/app.js b/ui/.quasar/prod-spa/app.js new file mode 100644 index 0000000..caeaac1 --- /dev/null +++ b/ui/.quasar/prod-spa/app.js @@ -0,0 +1,75 @@ +/* eslint-disable */ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config file > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + + + + +import { Quasar } from 'quasar' +import { markRaw } from 'vue' +import RootComponent from 'app/src/App.vue' + +import createStore from 'app/src/stores/index' +import createRouter from 'app/src/router/index' + + + + + +export default async function (createAppFn, quasarUserOptions) { + + + // Create the app instance. + // Here we inject into it the Quasar UI, the router & possibly the store. + const app = createAppFn(RootComponent) + + + + app.use(Quasar, quasarUserOptions) + + + + + const store = typeof createStore === 'function' + ? await createStore({}) + : createStore + + + app.use(store) + + + + + + const router = markRaw( + typeof createRouter === 'function' + ? await createRouter({store}) + : createRouter + ) + + + // make router instance available in store + + store.use(({ store }) => { store.router = router }) + + + + // Expose the app, the router and the store. + // Note that we are not mounting the app here, since bootstrapping will be + // different depending on whether we are in a browser or on the server. + return { + app, + store, + router + } +} diff --git a/ui/.quasar/prod-spa/client-entry.js b/ui/.quasar/prod-spa/client-entry.js new file mode 100644 index 0000000..5223e2b --- /dev/null +++ b/ui/.quasar/prod-spa/client-entry.js @@ -0,0 +1,158 @@ +/* eslint-disable */ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config file > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + +import { createApp } from 'vue' + + + + + + + +import '@quasar/extras/roboto-font/roboto-font.css' + +import '@quasar/extras/material-icons/material-icons.css' + + + + +// We load Quasar stylesheet file +import 'quasar/dist/quasar.sass' + + + + +import 'src/css/app.css' + + +import createQuasarApp from './app.js' +import quasarUserOptions from './quasar-user-options.js' + + + + + + + + +const publicPath = `/` + + +async function start ({ + app, + router + , store +}, bootFiles) { + + let hasRedirected = false + const getRedirectUrl = url => { + try { return router.resolve(url).href } + catch (err) {} + + return Object(url) === url + ? null + : url + } + const redirect = url => { + hasRedirected = true + + if (typeof url === 'string' && /^https?:\/\//.test(url)) { + window.location.href = url + return + } + + const href = getRedirectUrl(url) + + // continue if we didn't fail to resolve the url + if (href !== null) { + window.location.href = href + window.location.reload() + } + } + + const urlPath = window.location.href.replace(window.location.origin, '') + + for (let i = 0; hasRedirected === false && i < bootFiles.length; i++) { + try { + await bootFiles[i]({ + app, + router, + store, + ssrContext: null, + redirect, + urlPath, + publicPath + }) + } + catch (err) { + if (err && err.url) { + redirect(err.url) + return + } + + console.error('[Quasar] boot error:', err) + return + } + } + + if (hasRedirected === true) return + + + app.use(router) + + + + + + + app.mount('#q-app') + + + +} + +createQuasarApp(createApp, quasarUserOptions) + + .then(app => { + // eventually remove this when Cordova/Capacitor/Electron support becomes old + const [ method, mapFn ] = Promise.allSettled !== void 0 + ? [ + 'allSettled', + bootFiles => bootFiles.map(result => { + if (result.status === 'rejected') { + console.error('[Quasar] boot error:', result.reason) + return + } + return result.value.default + }) + ] + : [ + 'all', + bootFiles => bootFiles.map(entry => entry.default) + ] + + return Promise[ method ]([ + + import(/* webpackMode: "eager" */ 'boot/dayjs'), + + import(/* webpackMode: "eager" */ 'boot/locale'), + + import(/* webpackMode: "eager" */ 'boot/resizeObserverGuard') + + ]).then(bootFiles => { + const boot = mapFn(bootFiles).filter(entry => typeof entry === 'function') + start(app, boot) + }) + }) + diff --git a/ui/.quasar/prod-spa/client-prefetch.js b/ui/.quasar/prod-spa/client-prefetch.js new file mode 100644 index 0000000..9bbe3c5 --- /dev/null +++ b/ui/.quasar/prod-spa/client-prefetch.js @@ -0,0 +1,116 @@ +/* eslint-disable */ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config file > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + + + +import App from 'app/src/App.vue' +let appPrefetch = typeof App.preFetch === 'function' + ? App.preFetch + : ( + // Class components return the component options (and the preFetch hook) inside __c property + App.__c !== void 0 && typeof App.__c.preFetch === 'function' + ? App.__c.preFetch + : false + ) + + +function getMatchedComponents (to, router) { + const route = to + ? (to.matched ? to : router.resolve(to).route) + : router.currentRoute.value + + if (!route) { return [] } + + const matched = route.matched.filter(m => m.components !== void 0) + + if (matched.length === 0) { return [] } + + return Array.prototype.concat.apply([], matched.map(m => { + return Object.keys(m.components).map(key => { + const comp = m.components[key] + return { + path: m.path, + c: comp + } + }) + })) +} + +export function addPreFetchHooks ({ router, store, publicPath }) { + // Add router hook for handling preFetch. + // Doing it after initial route is resolved so that we don't double-fetch + // the data that we already have. Using router.beforeResolve() so that all + // async components are resolved. + router.beforeResolve((to, from, next) => { + const + urlPath = window.location.href.replace(window.location.origin, ''), + matched = getMatchedComponents(to, router), + prevMatched = getMatchedComponents(from, router) + + let diffed = false + const preFetchList = matched + .filter((m, i) => { + return diffed || (diffed = ( + !prevMatched[i] || + prevMatched[i].c !== m.c || + m.path.indexOf('/:') > -1 // does it has params? + )) + }) + .filter(m => m.c !== void 0 && ( + typeof m.c.preFetch === 'function' + // Class components return the component options (and the preFetch hook) inside __c property + || (m.c.__c !== void 0 && typeof m.c.__c.preFetch === 'function') + )) + .map(m => m.c.__c !== void 0 ? m.c.__c.preFetch : m.c.preFetch) + + + if (appPrefetch !== false) { + preFetchList.unshift(appPrefetch) + appPrefetch = false + } + + + if (preFetchList.length === 0) { + return next() + } + + let hasRedirected = false + const redirect = url => { + hasRedirected = true + next(url) + } + const proceed = () => { + + if (hasRedirected === false) { next() } + } + + + + preFetchList.reduce( + (promise, preFetch) => promise.then(() => hasRedirected === false && preFetch({ + store, + currentRoute: to, + previousRoute: from, + redirect, + urlPath, + publicPath + })), + Promise.resolve() + ) + .then(proceed) + .catch(e => { + console.error(e) + proceed() + }) + }) +} diff --git a/ui/.quasar/prod-spa/quasar-user-options.js b/ui/.quasar/prod-spa/quasar-user-options.js new file mode 100644 index 0000000..ac1dae3 --- /dev/null +++ b/ui/.quasar/prod-spa/quasar-user-options.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +/** + * THIS FILE IS GENERATED AUTOMATICALLY. + * DO NOT EDIT. + * + * You are probably looking on adding startup/initialization code. + * Use "quasar new boot " and add it there. + * One boot file per concern. Then reference the file(s) in quasar.config file > boot: + * boot: ['file', ...] // do not add ".js" extension to it. + * + * Boot files are your "main.js" + **/ + +import lang from 'quasar/lang/tr.js' + + + +import {Loading,Dialog,Notify} from 'quasar' + + + +export default { config: {"notify":{"position":"top","timeout":2500}},lang,plugins: {Loading,Dialog,Notify} } + diff --git a/ui/src/pages/ActivityLogs.vue b/ui/src/pages/ActivityLogs.vue index 843a064..c38703e 100644 --- a/ui/src/pages/ActivityLogs.vue +++ b/ui/src/pages/ActivityLogs.vue @@ -103,7 +103,10 @@ :rows="store.rows" :columns="columns" :loading="store.loading" + v-model:pagination="store.pagination" + :rows-per-page-options="[250, 500, 1000]" binary-state-sort + @request="store.onTableRequest" > diff --git a/ui/src/stores/activityLogStore.js b/ui/src/stores/activityLogStore.js index 9eb3c3c..2b088e8 100644 --- a/ui/src/stores/activityLogStore.js +++ b/ui/src/stores/activityLogStore.js @@ -4,18 +4,15 @@ import { get } from 'src/services/api' export const useActivityLogStore = defineStore('activityLogStore', { state: () => ({ loading: false, - rows: [], total: 0, - pagination: { page: 1, - rowsPerPage: 0, // ✅ SINIRSIZ + rowsPerPage: 1000, + rowsNumber: 0, sortBy: 'created_at', descending: true - } - , - + }, filters: { username: '', actionCategory: null, @@ -30,50 +27,34 @@ export const useActivityLogStore = defineStore('activityLogStore', { async fetchLogs () { this.loading = true try { - const params = {} - - if (this.pagination.rowsPerPage > 0) { - params.page = this.pagination.page - params.limit = this.pagination.rowsPerPage + const params = { + page: Math.max(1, Number(this.pagination.page || 1)), + limit: Math.min(1000, Math.max(1, Number(this.pagination.rowsPerPage || 1000))) } - - if (this.filters.username) - params.username = this.filters.username - - if (this.filters.actionCategory) - params.action_category = this.filters.actionCategory - - if (this.filters.actionType) - params.action_type = this.filters.actionType - - if (this.filters.success !== null) - params.success = this.filters.success - - if (this.filters.dateFrom) - params.date_from = this.filters.dateFrom - - if (this.filters.dateTo) - params.date_to = this.filters.dateTo + if (this.filters.username) params.username = this.filters.username + if (this.filters.actionCategory) params.action_category = this.filters.actionCategory + if (this.filters.actionType) params.action_type = this.filters.actionType + if (this.filters.success !== null) params.success = this.filters.success + if (this.filters.dateFrom) params.date_from = this.filters.dateFrom + if (this.filters.dateTo) params.date_to = this.filters.dateTo const data = await get('/activity-logs', params) this.rows = data.items || [] this.total = data.total || 0 + this.pagination.rowsNumber = this.total } finally { this.loading = false } }, - quickRoleChange () { + quickRoleChange () { this.filters.actionCategory = 'role_permission' this.filters.actionType = 'role_department_permission_change' - this.pagination.page = 1 - this.fetchLogs() - } - , + }, onTableRequest (props) { const { page, rowsPerPage, sortBy, descending } = props.pagination @@ -84,8 +65,8 @@ export const useActivityLogStore = defineStore('activityLogStore', { this.pagination.descending = descending this.fetchLogs() - } -, + }, + resetFilters () { this.filters = { username: '',