Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
318b16a
chore: fix typo in status message
AlexTMjugador Aug 18, 2025
a5e9797
feat(labrinth): overhaul malware scanner report storage and routes
AlexTMjugador Aug 18, 2025
14518f3
chore: address some review comments
AlexTMjugador Aug 22, 2025
de68c40
feat: add Delphi to Docker Compose `with-delphi` profile
AlexTMjugador Aug 22, 2025
8c7aa47
chore: fix unused import Clippy lint
AlexTMjugador Aug 22, 2025
a8166ef
feat(labrinth/delphi): use PAT token authorization with project read …
AlexTMjugador Aug 22, 2025
2589cc8
chore: expose file IDs in version queries
AlexTMjugador Aug 22, 2025
9d1f09e
fix: accept null decompiled source payloads from Delphi
AlexTMjugador Aug 23, 2025
10987dd
tweak(labrinth): expose base62 file IDs more consistently for Delphi
AlexTMjugador Aug 23, 2025
cd6ab8f
feat(labrinth/delphi): support new Delphi report severity field
AlexTMjugador Aug 23, 2025
6c0fe22
chore(labrinth): run `cargo sqlx prepare` to fix Docker build errors
AlexTMjugador Aug 25, 2025
4065acc
tweak: add route for fetching Delphi issue type schema, abstract Labr…
AlexTMjugador Sep 6, 2025
2e118d4
chore: run `cargo sqlx prepare`
AlexTMjugador Sep 6, 2025
d614e9c
chore: fix typo on frontend generated state file message
AlexTMjugador Oct 5, 2025
8c887c7
feat: update to use new Delphi issue schema
AlexTMjugador Oct 24, 2025
6720334
wip: tech review endpoints
aecsocket Nov 7, 2025
356d76a
wip: add ToSchema for dependent types
aecsocket Nov 10, 2025
3ce5c23
wip: report issues return
aecsocket Nov 10, 2025
846f2cf
wip
aecsocket Nov 12, 2025
2aac5ab
wip: returning more data
aecsocket Nov 13, 2025
a487495
wip
aecsocket Nov 13, 2025
54c053a
Fix up db query
aecsocket Nov 13, 2025
3464333
Delphi configuration to talk to Labrinth
aecsocket Nov 13, 2025
3c792fc
Get Delphi working with Labrinth
aecsocket Nov 15, 2025
8986a1f
Add Delphi dummy fixture
aecsocket Nov 15, 2025
da866a4
Better Delphi logging
aecsocket Nov 15, 2025
b2fceb6
Improve utoipa for tech review routes
aecsocket Nov 16, 2025
3ec0a50
Add more sorting options for tech review queue
aecsocket Nov 16, 2025
49c4d37
Oops join
aecsocket Nov 16, 2025
945d4be
New routes for fetching issues and reports
aecsocket Nov 18, 2025
18aca82
Fix which kind of ID is returned in tech review endpoints
aecsocket Nov 19, 2025
4c66b9f
Deduplicate tech review report rows
aecsocket Nov 19, 2025
a59b18c
Reduce info sent for projects
aecsocket Nov 19, 2025
c807076
Fetch more thread info
aecsocket Nov 21, 2025
560a6e0
Address PR comments
aecsocket Nov 23, 2025
081f094
fix ci
aecsocket Nov 25, 2025
ed50f34
fix ci
aecsocket Nov 25, 2025
17e3c12
fix postgres version mismatch
aecsocket Nov 26, 2025
6457674
fix version creation
aecsocket Nov 26, 2025
8dd600a
Implement routes
aecsocket Nov 30, 2025
3a955d7
feat: batch scan alert
IMB11 Sep 13, 2025
557b1f8
feat: layout
IMB11 Sep 16, 2025
d39feda
feat: introduce surface variables
IMB11 Sep 18, 2025
2d4a8c1
fix: theme selector
IMB11 Sep 18, 2025
1056f42
feat: rough draft of tech review card
IMB11 Sep 19, 2025
b807b8d
feat: tab switcher
IMB11 Sep 21, 2025
b95df97
feat: batch scan btn
IMB11 Sep 21, 2025
206b0ae
feat: api-client module for tech review
IMB11 Nov 15, 2025
f3ff192
draft: impl
IMB11 Nov 15, 2025
fa94fe5
feat: auto icons
IMB11 Nov 15, 2025
98efd84
fix: layout issues
IMB11 Nov 15, 2025
a0347ea
feat: fixes to code blocks + flag labels
IMB11 Nov 16, 2025
c80262f
feat: temp remove mock data
IMB11 Nov 16, 2025
3a8a951
fix: search sort types
IMB11 Nov 16, 2025
42e496a
fix: intl & lint
IMB11 Nov 16, 2025
eb8a118
chore: re-enable mock data
IMB11 Nov 17, 2025
9e6b09b
fix: flag badges + auto open first issue in file tab
IMB11 Nov 17, 2025
113630d
feat: update for new routes
IMB11 Nov 19, 2025
07546d1
fix: more qa issues
IMB11 Nov 19, 2025
7180bbd
feat: lazy load sources
IMB11 Nov 19, 2025
9c9a4e9
fix: re-enable auth middleware
IMB11 Nov 21, 2025
2d68bda
feat: impl threads
IMB11 Nov 22, 2025
2a63d96
fix: lint & severity
IMB11 Nov 22, 2025
354e4ef
feat: download btn + switch to using NavTabs with new local mode option
IMB11 Nov 24, 2025
154d6d1
feat: re-add toplevel btns
IMB11 Nov 25, 2025
c2f194c
feat: reports page consistency
IMB11 Nov 28, 2025
8052a41
fix: consistency on project queue
IMB11 Nov 28, 2025
f75dbf1
fix: icons + sizing
IMB11 Nov 28, 2025
5480409
fix: colors and gaps
IMB11 Nov 28, 2025
177e921
fix: impl endpoints
IMB11 Nov 30, 2025
a80613f
feat: load all flags on file tab
IMB11 Dec 3, 2025
c3d195c
Merge branch 'boris/tech-review-queue' into cal/dev-434
aecsocket Dec 4, 2025
6183cf2
feat: thread generics changes
IMB11 Dec 5, 2025
1530b49
feat: more qa
IMB11 Dec 5, 2025
429c44d
feat: fix collapse
IMB11 Dec 5, 2025
b182d8f
fix: qa
IMB11 Dec 5, 2025
8191173
feat: msg modal
IMB11 Dec 5, 2025
13cf3d3
fix: ISO import
IMB11 Dec 5, 2025
5c13eb3
feat: qa fixes
IMB11 Dec 8, 2025
2b8b298
fix: empty state basic
IMB11 Dec 8, 2025
126d9fe
fix: collapsible region
IMB11 Dec 8, 2025
42f7581
fix: collapse thread by default
IMB11 Dec 9, 2025
6ab9d9b
feat: rough draft of new process/flow
IMB11 Dec 11, 2025
e7c6abf
Merge branch 'boris/tech-review-queue' into cal/dev-434
IMB11 Dec 11, 2025
8263751
Merge branch 'boris/tech-review-queue' into cal/dev-434
aecsocket Dec 11, 2025
16cbd39
fix labrinth build
aecsocket Dec 11, 2025
cdee189
fix thread message privacy
aecsocket Dec 11, 2025
bb37838
New tech review search route
aecsocket Dec 12, 2025
aac0934
Merge branch 'boris/tech-review-queue' into cal/dev-434
aecsocket Dec 12, 2025
11dec76
feat: qa fixes
IMB11 Dec 12, 2025
4c5cff3
feat: QA changes
IMB11 Dec 13, 2025
7eb7a9f
fix: verdict on detail not whole issue
IMB11 Dec 13, 2025
f208082
fix: lint + intl
IMB11 Dec 13, 2025
3b6d676
fix: lint
IMB11 Dec 15, 2025
caf9d95
fix: thread message for tech rev verdict
IMB11 Dec 15, 2025
42c5945
feat: use anim frames
IMB11 Dec 15, 2025
7ac170c
Merge remote-tracking branch 'origin/boris/tech-review-queue' into ca…
IMB11 Dec 15, 2025
0abba65
fix: exports + typecheck
IMB11 Dec 16, 2025
0f99fee
polish: qa changes
IMB11 Dec 16, 2025
cc54ed9
feat: qa
IMB11 Dec 17, 2025
24210c3
Merge remote-tracking branch 'origin/boris/tech-review-queue' into ca…
IMB11 Dec 17, 2025
f335dc0
feat: qa polish
IMB11 Dec 18, 2025
8e998e0
feat: fix malic modal
IMB11 Dec 18, 2025
9b3b110
fix: lint
IMB11 Dec 18, 2025
063ce76
fix: qa + lint
IMB11 Dec 18, 2025
8c3a3c6
fix: pagination
IMB11 Dec 18, 2025
bea8b60
fix: lint
IMB11 Dec 18, 2025
cdee093
fix: qa
IMB11 Dec 19, 2025
607c26d
intl extract
aecsocket Dec 20, 2025
a4a9675
Merge branch 'boris/tech-review-queue' into cal/dev-434
aecsocket Dec 20, 2025
09aa466
Merge branch 'boris/tech-review-queue' into cal/dev-434
aecsocket Dec 20, 2025
5c6105b
fix ci
aecsocket Dec 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 126 additions & 43 deletions apps/frontend/src/components/ui/NavTabs.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,58 @@
<template>
<nav
ref="scrollContainer"
class="card-shadow experimental-styles-within relative flex w-fit overflow-x-auto rounded-full bg-bg-raised p-1 text-sm font-bold"
class="experimental-styles-within relative flex w-fit overflow-x-auto rounded-full bg-bg-raised p-1 text-sm font-bold"
:class="[mode === 'navigation' ? 'card-shadow' : undefined]"
>
<NuxtLink
v-for="(link, index) in filteredLinks"
v-show="link.shown === undefined ? true : link.shown"
:key="index"
ref="tabLinkElements"
:to="query ? (link.href ? `?${query}=${link.href}` : '?') : link.href"
class="button-animation z-[1] flex flex-row items-center gap-2 px-4 py-2 focus:rounded-full"
:class="{
'text-button-textSelected': activeIndex === index && !subpageSelected,
'text-contrast': activeIndex === index && subpageSelected,
}"
>
<component :is="link.icon" v-if="link.icon" class="size-5" />
<span class="text-nowrap">{{ link.label }}</span>
</NuxtLink>
<template v-if="mode === 'navigation'">
<NuxtLink
v-for="(link, index) in filteredLinks"
v-show="link.shown === undefined ? true : link.shown"
:key="link.href"
ref="tabLinkElements"
:to="query ? (link.href ? `?${query}=${link.href}` : '?') : link.href"
class="button-animation z-[1] flex flex-row items-center gap-2 px-4 py-2 focus:rounded-full"
>
<component
:is="link.icon"
v-if="link.icon"
class="size-5"
:class="{
'text-brand': currentActiveIndex === index && !subpageSelected,
'text-secondary': currentActiveIndex !== index || subpageSelected,
}"
/>
<span class="text-nowrap text-contrast">{{ link.label }}</span>
</NuxtLink>
</template>
<template v-else>
<div
v-for="(link, index) in filteredLinks"
v-show="link.shown === undefined ? true : link.shown"
:key="link.href"
ref="tabLinkElements"
class="button-animation z-[1] flex flex-row items-center gap-2 px-4 py-2 hover:cursor-pointer focus:rounded-full"
@click="emit('tabClick', index, link)"
>
<component
:is="link.icon"
v-if="link.icon"
class="size-5"
:class="{
'text-brand': currentActiveIndex === index && !subpageSelected,
'text-secondary': currentActiveIndex !== index || subpageSelected,
}"
/>
<span
class="text-nowrap"
:class="{
'text-brand': currentActiveIndex === index && !subpageSelected,
'text-contrast': currentActiveIndex !== index || subpageSelected,
}"
>{{ link.label }}</span
>
</div>
</template>
<div
:class="`navtabs-transition pointer-events-none absolute h-[calc(100%-0.5rem)] overflow-hidden rounded-full p-1 ${
subpageSelected ? 'bg-button-bg' : 'bg-button-bgSelected'
Expand All @@ -27,29 +62,44 @@
top: sliderTopPx,
right: sliderRightPx,
bottom: sliderBottomPx,
opacity: sliderLeft === 4 && sliderLeft === sliderRight ? 0 : activeIndex === -1 ? 0 : 1,
opacity:
sliderLeft === 4 && sliderLeft === sliderRight ? 0 : currentActiveIndex === -1 ? 0 : 1,
}"
aria-hidden="true"
></div>
</nav>
</template>

<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import type { Component } from 'vue'
import { computed, nextTick, onMounted, ref, watch } from 'vue'

const route = useNativeRoute()

interface Tab {
label: string
href: string
shown?: boolean
icon?: string
icon?: Component
subpages?: string[]
}

const props = defineProps<{
links: Tab[]
query?: string
const props = withDefaults(
defineProps<{
links: Tab[]
query?: string
mode?: 'navigation' | 'local'
activeIndex?: number
}>(),
{
mode: 'navigation',
query: undefined,
activeIndex: undefined,
},
)

const emit = defineEmits<{
tabClick: [index: number, tab: Tab]
}>()

const scrollContainer = ref<HTMLElement | null>(null)
Expand All @@ -58,7 +108,7 @@ const sliderLeft = ref(4)
const sliderTop = ref(4)
const sliderRight = ref(4)
const sliderBottom = ref(4)
const activeIndex = ref(-1)
const currentActiveIndex = ref(-1)
const subpageSelected = ref(false)

const filteredLinks = computed(() =>
Expand All @@ -74,38 +124,49 @@ const tabLinkElements = ref()
function pickLink() {
let index = -1
subpageSelected.value = false
for (let i = filteredLinks.value.length - 1; i >= 0; i--) {
const link = filteredLinks.value[i]
if (props.query) {
if (route.query[props.query] === link.href || (!route.query[props.query] && !link.href)) {

if (props.mode === 'local' && props.activeIndex !== undefined) {
index = Math.min(props.activeIndex, filteredLinks.value.length - 1)
} else {
for (let i = filteredLinks.value.length - 1; i >= 0; i--) {
const link = filteredLinks.value[i]
if (props.query) {
if (route.query[props.query] === link.href || (!route.query[props.query] && !link.href)) {
index = i
break
}
} else if (decodeURIComponent(route.path) === link.href) {
index = i
break
} else if (
decodeURIComponent(route.path).includes(link.href) ||
(link.subpages &&
link.subpages.some((subpage) => decodeURIComponent(route.path).includes(subpage)))
) {
index = i
subpageSelected.value = true
break
}
} else if (decodeURIComponent(route.path) === link.href) {
index = i
break
} else if (
decodeURIComponent(route.path).includes(link.href) ||
(link.subpages &&
link.subpages.some((subpage) => decodeURIComponent(route.path).includes(subpage)))
) {
index = i
subpageSelected.value = true
break
}
}
activeIndex.value = index

if (activeIndex.value !== -1) {
startAnimation()
currentActiveIndex.value = index

if (currentActiveIndex.value !== -1) {
nextTick(() => startAnimation())
} else {
sliderLeft.value = 0
sliderRight.value = 0
}
}

function startAnimation() {
const el = tabLinkElements.value[activeIndex.value]?.$el
// In navigation mode, elements are NuxtLinks with $el property
// In local mode, elements are plain divs
const el =
props.mode === 'navigation'
? tabLinkElements.value[currentActiveIndex.value]?.$el
: tabLinkElements.value[currentActiveIndex.value]

if (!el || !el.offsetParent) return

Expand Down Expand Up @@ -156,7 +217,29 @@ onMounted(() => {

watch(
() => [route.path, route.query],
() => pickLink(),
() => {
if (props.mode === 'navigation') {
pickLink()
}
},
)

watch(
() => props.activeIndex,
() => {
if (props.mode === 'local') {
pickLink()
}
},
)

watch(
() => props.links,
() => {
// Re-trigger animation when links change
pickLink()
},
{ deep: true },
)
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ import {
financialMessages,
formFieldLabels,
formFieldPlaceholders,
getBlockchainColor,
getBlockchainIcon,
normalizeChildren,
} from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
Expand All @@ -218,12 +220,6 @@ import RevenueInputField from '@/components/ui/dashboard/RevenueInputField.vue'
import WithdrawFeeBreakdown from '@/components/ui/dashboard/WithdrawFeeBreakdown.vue'
import { useGeneratedState } from '@/composables/generated'
import { useWithdrawContext } from '@/providers/creator-withdraw.ts'
import {
getBlockchainColor,
getBlockchainIcon,
getCurrencyColor,
getCurrencyIcon,
} from '@/utils/finance-icons.ts'
import { getRailConfig } from '@/utils/muralpay-rails'

const { withdrawData, maxWithdrawAmount, availableMethods, calculateFees } = useWithdrawContext()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<template>
<div
class="flex flex-col gap-4 rounded-2xl border-[1px] border-solid border-blue bg-highlight-blue p-4"
>
<div class="flex flex-row justify-between">
<div class="flex flex-col text-contrast">
<span class="text-xl font-semibold">Batch scan in progress</span>
<span>{{ progress?.complete }} of {{ progress?.total }} projects completed</span>
</div>
<ButtonStyled circular color="blue" type="outlined">
<button class="!px-4" @click="emit('cancel-scan')">Cancel scan</button>
</ButtonStyled>
</div>
<div class="w-full rounded-full bg-highlight-blue">
<div
class="h-3 rounded-[inherit] bg-blue"
:style="`width: ${((progress?.complete ?? 0) / (progress?.total ?? 1)) * 100}%`"
/>
</div>
</div>
</template>

<script setup lang="ts">
import { ButtonStyled } from '@modrinth/ui'
import { defineProps } from 'vue'

export interface BatchScanProgress {
total: number
complete: number
}

defineProps<{
progress?: BatchScanProgress
}>()

const emit = defineEmits<{
(e: 'cancel-scan'): void
}>()
</script>
Loading