Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp Changes Log page #64

Closed
wants to merge 11 commits into from
4 changes: 1 addition & 3 deletions app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ catch (err: any) {
<nuxt-layout>
<el-container direction="vertical">
<app-menu />
<el-main>
<nuxt-page />
</el-main>
<nuxt-page />
<app-footer />
</el-container>
</nuxt-layout>
Expand Down
191 changes: 191 additions & 0 deletions components/LogFilters.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<script setup lang="ts">
import { countBy, indexBy, sortBy, uniq } from 'underscore'
import type { LocationQuery } from 'vue-router'
import type { Log } from '~/libs/types'

const logs = useState<Log[]>('logs')

const route = useRoute()
const filters = ref<LocationQuery>()
watchEffect(() => {
filters.value = route.query
})

const stats = computed((): [string, number][] => {
const actions = logs.value
.map((log) =>
uniq(
[
...Object.values(log.diff_attribs || {}),
...Object.values(log.diff_tags || {}),
]
.flat(1)
.map((action) => action[0]),
),
)
.flat(1)
return getStats(actions)
})

const statUserGroups = computed(() => {
const userGroups = logs.value
.map((log) => uniq(log.matches.map((m) => m.user_groups).flat(2)))
.flat(1)
return getStats(userGroups)
})

const statSelectors = computed(() => {
const matches = logs.value.map((log) => uniq(log.matches).flat()).flat(1)
return getStats(matches, (m) => m.selectors.join(';'))
})

const statUsers = computed(() => {
const users = logs.value
.map((log) =>
(log.base ? log.changesets.slice(1) : log.changesets).map(
(changeset) => changeset.user,
),
)
.flat(2)
return getStats(users)
})

const statDates = computed(() => {
const dates = logs.value.map((log) => log.change.created.substring(0, 10))
return getStats(dates).sort()
})

function getStats<Type>(
data: Type[],
key: (o: Type) => string = (i) => `${i}`,
): [Type, number][] {
const index = indexBy(data, key)
return sortBy(
Object.entries(countBy(data, key)) as [string, number][],
([_key, count]) => -count,
).map(([key, count]) => [index[key], count])
}

const router = useRouter()
async function applyFilter(key: string, value: string) {
const query = filters.value?.[key] === value
? Object.fromEntries(Object.entries(route.query).filter(([k, _v]) => k !== key))
: { ...route.query, [key]: value }

await router.replace({ ...route, query })
}
</script>

<template>
<aside>
<h3>{{ $t('logs.filters') }}</h3>
<el-row style="margin-top: 20px">
<el-badge :value="logs.length" class="item" :max="999">
<el-tag size="small">
{{ $t('logs.objects') }}
</el-tag>
</el-badge>
<el-badge
v-for="[filter, count] in stats"
:key="filter"
:value="count"
class="item"
:max="999"
>
<el-button
type="danger"
size="small"
:plain="filters?.filterByAction !== filter"
:disabled="!!(filters?.filterByAction && filters?.filterByAction !== filter)"
@click="applyFilter('filterByAction', filter)"
>
{{ filter }}
</el-button>
</el-badge>
</el-row>
<el-row>
<el-badge
v-for="[filter, count] in statUserGroups"
:key="filter"
:value="count"
class="item"
:max="999"
>
<el-button
type="primary"
size="small"
:plain="filters?.filterByUserGroups !== filter"
:disabled="!!(filters?.filterByUserGroups && filters?.filterByUserGroups !== filter)"
@click="applyFilter('filterByUserGroups', filter)"
>
📌 {{ filter }}
</el-button>
</el-badge>
</el-row>
<el-row>
<el-badge
v-for="[match, count] in statSelectors"
:key="match.selectors.join()"
:value="count"
class="item"
:max="999"
>
<el-button
type="warning"
:plain="filters?.filterBySelectors !== match.selectors.join()"
:disabled="!!(filters?.filterBySelectors && filters?.filterBySelectors !== match.selectors.join())"
size="small"
@click="applyFilter('filterBySelectors', match.selectors.join())"
>
🏷️ {{ $i18nHash(match.name) || match.selectors.join() }}
</el-button>
</el-badge>
</el-row>
<el-row>
<el-badge
v-for="[filter, count] in statUsers.slice(0, 20)"
:key="filter"
:value="count"
class="item"
:max="999"
>
<el-button
type="info"
:plain="filters?.filterByUsers !== filter"
:disabled="!!(filters?.filterByUsers && filters?.filterByUsers !== filter)"
size="small"
@click="applyFilter('filterByUsers', filter)"
>
👤 {{ filter }}
</el-button>
</el-badge>
<span v-if="statUsers.length > 20">{{ $t('logs.tags_more') }}</span>
</el-row>
<el-row>
<el-badge
v-for="[filter, count] in statDates"
:key="filter"
:value="count"
class="item"
:max="999"
>
<el-button
type="primary"
:plain="filters?.filterByDate !== filter"
:disabled="!!(filters?.filterByDate && filters?.filterByDate !== filter)"
size="small"
@click="applyFilter('filterByDate', filter)"
>
📅 {{ filter }}
</el-button>
</el-badge>
</el-row>
</aside>
</template>

<style scoped>
.item {
margin-top: 0.7em;
margin-right: 1.3em;
}
</style>
63 changes: 20 additions & 43 deletions components/LogsComponent.vue → components/LogItem.vue
Original file line number Diff line number Diff line change
@@ -1,45 +1,26 @@
<script lang="ts">
import type { PropType } from 'vue'
<script setup lang="ts">
import LazyComponent from 'v-lazy-component'
import Diff from '~/components/Diff.vue'
import type { Log, ObjType, ObjTypeFull, ObjectId } from '~/libs/types'
import { objTypeFull } from '~/libs/types'
import Changesets from '~/components/Changesets.vue'

export default defineNuxtComponent({
name: 'LogsComponent',
const props = defineProps<{
log: Log
project: string
projectUser: boolean
}>()

components: {
Changesets,
Diff,
LazyComponent,
},
defineEmits<{
(e: 'accept', objectId: ObjectId): void
}>()

props: {
log: {
type: Object as PropType<Log>,
required: true,
},
project: {
type: String,
required: true,
},
projectUser: {
type: Boolean,
required: true,
},
},

emits: {
accept: (_objectId: ObjectId) => true,
},

methods: {
objtypeFull(objtype: ObjType): ObjTypeFull {
return objTypeFull(objtype)
},
},
const logSorted = computed(() => {
const sorted = props.log
return sorted.matches.sort()
})

function objtypeFull(objtype: ObjType): ObjTypeFull {
return objTypeFull(objtype)
}
</script>

<template>
Expand All @@ -48,9 +29,7 @@ export default defineNuxtComponent({
<div class="card-header">
<span>
<a
:href="`https://www.openstreetmap.org/${objtypeFull(log.objtype)}/${
log.id
}/history`"
:href="`https://www.openstreetmap.org/${objtypeFull(log.objtype)}/${log.id}/history`"
target="_blank"
>
{{ log.objtype }}{{ log.id }}
Expand Down Expand Up @@ -90,7 +69,7 @@ export default defineNuxtComponent({
📌 {{ text }}
</el-tag>
<el-tag
v-for="match in log.matches.sort()"
v-for="match in logSorted"
:key="match.selectors.join(';')"
size="small"
type="warning"
Expand Down Expand Up @@ -176,12 +155,10 @@ export default defineNuxtComponent({
</template>v{{
log.change.version
}}
<Changesets
:changesets="log.base ? log.changesets.slice(1) : log.changesets"
/>
<changesets :changesets="log.base ? log.changesets.slice(1) : log.changesets" />
</el-col>
<el-col :span="7">
<Diff
<diff
:src="log.base"
:dst="log.change"
:diff="log.diff_attribs || {}"
Expand Down
37 changes: 37 additions & 0 deletions components/LogList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script setup lang="ts">
import type { Log, User } from '~/libs/types'

const props = defineProps<{
logs: Log[]
projectSlug: string
}>()

defineEmits(['validate'])

const user = useState<User>('user')
const isProjectUser = computed(() => {
if (!user.value) {
return false
}
return !!user.value.projects?.includes(props.projectSlug)
})

const scrollCount = ref(2)
const lazyLogs = computed(() => props.logs.slice(0, scrollCount.value))
function scrollLoad() {
scrollCount.value += 2
}
</script>

<template>
<el-space v-infinite-scroll="scrollLoad" :fill="true" wrap :size="20">
<log-item
v-for="log in lazyLogs"
:key="log.id"
:log="log"
:project="projectSlug"
:project-user="isProjectUser"
@accept="$emit('validate', $event)"
/>
</el-space>
</template>
11 changes: 11 additions & 0 deletions components/LogValidatorBulk.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts">
defineEmits(['bulkValidation'])
</script>

<template>
<el-button-group>
<el-button type="primary" @click="$emit('bulkValidation')">
✓ {{ $t('logs.validate_selection') }}
</el-button>
</el-button-group>
</template>
Loading