setup for rank change page
This commit is contained in:
32
ui/package-lock.json
generated
32
ui/package-lock.json
generated
@@ -11,12 +11,12 @@
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@vee-validate/zod": "^4.15.1",
|
||||
"@vueuse/core": "^13.7.0",
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-vue-next": "^0.539.0",
|
||||
"pinia": "^3.0.3",
|
||||
"reka-ui": "^2.4.1",
|
||||
"reka-ui": "^2.5.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tw-animate-css": "^1.3.6",
|
||||
@@ -1982,14 +1982,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.7.0.tgz",
|
||||
"integrity": "sha512-myagn09+c6BmS6yHc1gTwwsdZilAovHslMjyykmZH3JNyzI5HoWhv114IIdytXiPipdHJ2gDUx0PB93jRduJYg==",
|
||||
"version": "13.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.9.0.tgz",
|
||||
"integrity": "sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.21",
|
||||
"@vueuse/metadata": "13.7.0",
|
||||
"@vueuse/shared": "13.7.0"
|
||||
"@vueuse/metadata": "13.9.0",
|
||||
"@vueuse/shared": "13.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
@@ -1999,18 +1999,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.7.0.tgz",
|
||||
"integrity": "sha512-8okFhS/1ite8EwUdZZfvTYowNTfXmVCOrBFlA31O0HD8HKXhY+WtTRyF0LwbpJfoFPc+s9anNJIXMVrvP7UTZg==",
|
||||
"version": "13.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.9.0.tgz",
|
||||
"integrity": "sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.7.0.tgz",
|
||||
"integrity": "sha512-Wi2LpJi4UA9kM0OZ0FCZslACp92HlVNw1KPaDY6RAzvQ+J1s7seOtcOpmkfbD5aBSmMn9NvOakc8ZxMxmDXTIg==",
|
||||
"version": "13.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.9.0.tgz",
|
||||
"integrity": "sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
@@ -3171,9 +3171,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/reka-ui": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.4.1.tgz",
|
||||
"integrity": "sha512-NB7DrCsODN8MH02BWtgiExygfFcuuZ5/PTn6fMgjppmFHqePvNhmSn1LEuF35nel6PFbA4v+gdj0IoGN1yZ+vw==",
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.5.0.tgz",
|
||||
"integrity": "sha512-81aMAmJeVCy2k0E6x7n1kypDY6aM1ldLis5+zcdV1/JtoAlSDck5OBsyLRJU9CfgbrQp1ImnRnBSmC4fZ2fkZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@vee-validate/zod": "^4.15.1",
|
||||
"@vueuse/core": "^13.7.0",
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-vue-next": "^0.539.0",
|
||||
"pinia": "^3.0.3",
|
||||
"reka-ui": "^2.4.1",
|
||||
"reka-ui": "^2.5.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tw-animate-css": "^1.3.6",
|
||||
|
||||
0
ui/src/api/rank.ts
Normal file
0
ui/src/api/rank.ts
Normal file
33
ui/src/components/ui/combobox/Combobox.vue
Normal file
33
ui/src/components/ui/combobox/Combobox.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<script setup>
|
||||
import { ComboboxRoot, useForwardPropsEmits } from "reka-ui";
|
||||
|
||||
const props = defineProps({
|
||||
open: { type: Boolean, required: false },
|
||||
defaultOpen: { type: Boolean, required: false },
|
||||
resetSearchTermOnBlur: { type: Boolean, required: false },
|
||||
resetSearchTermOnSelect: { type: Boolean, required: false },
|
||||
openOnFocus: { type: Boolean, required: false },
|
||||
openOnClick: { type: Boolean, required: false },
|
||||
ignoreFilter: { type: Boolean, required: false },
|
||||
modelValue: { type: null, required: false },
|
||||
defaultValue: { type: null, required: false },
|
||||
multiple: { type: Boolean, required: false },
|
||||
dir: { type: String, required: false },
|
||||
disabled: { type: Boolean, required: false },
|
||||
highlightOnHover: { type: Boolean, required: false },
|
||||
by: { type: [String, Function], required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
name: { type: String, required: false },
|
||||
required: { type: Boolean, required: false },
|
||||
});
|
||||
const emits = defineEmits(["update:modelValue", "highlight", "update:open"]);
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxRoot data-slot="combobox" v-bind="forwarded">
|
||||
<slot />
|
||||
</ComboboxRoot>
|
||||
</template>
|
||||
26
ui/src/components/ui/combobox/ComboboxAnchor.vue
Normal file
26
ui/src/components/ui/combobox/ComboboxAnchor.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxAnchor, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
reference: { type: null, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxAnchor
|
||||
data-slot="combobox-anchor"
|
||||
v-bind="forwarded"
|
||||
:class="cn('w-[200px]', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxAnchor>
|
||||
</template>
|
||||
23
ui/src/components/ui/combobox/ComboboxEmpty.vue
Normal file
23
ui/src/components/ui/combobox/ComboboxEmpty.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxEmpty } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxEmpty
|
||||
data-slot="combobox-empty"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('py-6 text-center text-sm', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxEmpty>
|
||||
</template>
|
||||
30
ui/src/components/ui/combobox/ComboboxGroup.vue
Normal file
30
ui/src/components/ui/combobox/ComboboxGroup.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxGroup, ComboboxLabel } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
heading: { type: String, required: false },
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxGroup
|
||||
data-slot="combobox-group"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('overflow-hidden p-1 text-foreground', props.class)"
|
||||
>
|
||||
<ComboboxLabel
|
||||
v-if="heading"
|
||||
class="px-2 py-1.5 text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{{ heading }}
|
||||
</ComboboxLabel>
|
||||
<slot />
|
||||
</ComboboxGroup>
|
||||
</template>
|
||||
47
ui/src/components/ui/combobox/ComboboxInput.vue
Normal file
47
ui/src/components/ui/combobox/ComboboxInput.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { SearchIcon } from "lucide-vue-next";
|
||||
import { ComboboxInput, useForwardPropsEmits } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
displayValue: { type: Function, required: false },
|
||||
modelValue: { type: String, required: false },
|
||||
autoFocus: { type: Boolean, required: false },
|
||||
disabled: { type: Boolean, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-slot="command-input-wrapper"
|
||||
class="flex h-9 items-center gap-2 border-b px-3"
|
||||
>
|
||||
<SearchIcon class="size-4 shrink-0 opacity-50" />
|
||||
<ComboboxInput
|
||||
data-slot="command-input"
|
||||
:class="
|
||||
cn(
|
||||
'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxInput>
|
||||
</div>
|
||||
</template>
|
||||
34
ui/src/components/ui/combobox/ComboboxItem.vue
Normal file
34
ui/src/components/ui/combobox/ComboboxItem.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxItem, useForwardPropsEmits } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
textValue: { type: String, required: false },
|
||||
value: { type: null, required: true },
|
||||
disabled: { type: Boolean, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
const emits = defineEmits(["select"]);
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxItem
|
||||
data-slot="combobox-item"
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
`data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxItem>
|
||||
</template>
|
||||
25
ui/src/components/ui/combobox/ComboboxItemIndicator.vue
Normal file
25
ui/src/components/ui/combobox/ComboboxItemIndicator.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxItemIndicator, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxItemIndicator
|
||||
data-slot="combobox-item-indicator"
|
||||
v-bind="forwarded"
|
||||
:class="cn('ml-auto', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxItemIndicator>
|
||||
</template>
|
||||
58
ui/src/components/ui/combobox/ComboboxList.vue
Normal file
58
ui/src/components/ui/combobox/ComboboxList.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxContent, ComboboxPortal, useForwardPropsEmits } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
forceMount: { type: Boolean, required: false },
|
||||
position: { type: String, required: false, default: "popper" },
|
||||
bodyLock: { type: Boolean, required: false },
|
||||
side: { type: null, required: false },
|
||||
sideOffset: { type: Number, required: false, default: 4 },
|
||||
sideFlip: { type: Boolean, required: false },
|
||||
align: { type: null, required: false, default: "center" },
|
||||
alignOffset: { type: Number, required: false },
|
||||
alignFlip: { type: Boolean, required: false },
|
||||
avoidCollisions: { type: Boolean, required: false },
|
||||
collisionBoundary: { type: null, required: false },
|
||||
collisionPadding: { type: [Number, Object], required: false },
|
||||
arrowPadding: { type: Number, required: false },
|
||||
sticky: { type: String, required: false },
|
||||
hideWhenDetached: { type: Boolean, required: false },
|
||||
positionStrategy: { type: String, required: false },
|
||||
updatePositionStrategy: { type: String, required: false },
|
||||
disableUpdateOnLayoutShift: { type: Boolean, required: false },
|
||||
prioritizePosition: { type: Boolean, required: false },
|
||||
reference: { type: null, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
disableOutsidePointerEvents: { type: Boolean, required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
const emits = defineEmits([
|
||||
"escapeKeyDown",
|
||||
"pointerDownOutside",
|
||||
"focusOutside",
|
||||
"interactOutside",
|
||||
]);
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxPortal>
|
||||
<ComboboxContent
|
||||
data-slot="combobox-list"
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 w-[200px] rounded-md border bg-popover text-popover-foreground origin-(--reka-combobox-content-transform-origin) overflow-hidden shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxContent>
|
||||
</ComboboxPortal>
|
||||
</template>
|
||||
23
ui/src/components/ui/combobox/ComboboxSeparator.vue
Normal file
23
ui/src/components/ui/combobox/ComboboxSeparator.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxSeparator } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxSeparator
|
||||
data-slot="combobox-separator"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('bg-border -mx-1 h-px', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxSeparator>
|
||||
</template>
|
||||
27
ui/src/components/ui/combobox/ComboboxTrigger.vue
Normal file
27
ui/src/components/ui/combobox/ComboboxTrigger.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxTrigger, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
disabled: { type: Boolean, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxTrigger
|
||||
data-slot="combobox-trigger"
|
||||
v-bind="forwarded"
|
||||
:class="cn('', props.class)"
|
||||
tabindex="0"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxTrigger>
|
||||
</template>
|
||||
31
ui/src/components/ui/combobox/ComboboxViewport.vue
Normal file
31
ui/src/components/ui/combobox/ComboboxViewport.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<script setup>
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ComboboxViewport, useForwardProps } from "reka-ui";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps({
|
||||
nonce: { type: String, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: [String, Object, Function], required: false },
|
||||
class: { type: null, required: false },
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class");
|
||||
|
||||
const forwarded = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ComboboxViewport
|
||||
data-slot="combobox-viewport"
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</ComboboxViewport>
|
||||
</template>
|
||||
12
ui/src/components/ui/combobox/index.js
Normal file
12
ui/src/components/ui/combobox/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
export { default as Combobox } from "./Combobox.vue";
|
||||
export { default as ComboboxAnchor } from "./ComboboxAnchor.vue";
|
||||
export { default as ComboboxEmpty } from "./ComboboxEmpty.vue";
|
||||
export { default as ComboboxGroup } from "./ComboboxGroup.vue";
|
||||
export { default as ComboboxInput } from "./ComboboxInput.vue";
|
||||
export { default as ComboboxItem } from "./ComboboxItem.vue";
|
||||
export { default as ComboboxItemIndicator } from "./ComboboxItemIndicator.vue";
|
||||
export { default as ComboboxList } from "./ComboboxList.vue";
|
||||
export { default as ComboboxSeparator } from "./ComboboxSeparator.vue";
|
||||
export { default as ComboboxViewport } from "./ComboboxViewport.vue";
|
||||
|
||||
export { ComboboxCancel, ComboboxTrigger } from "reka-ui";
|
||||
@@ -2,8 +2,10 @@
|
||||
import ApplicationChat from '@/components/application/ApplicationChat.vue';
|
||||
import ApplicationForm from '@/components/application/ApplicationForm.vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { ApplicationData, loadApplication, postApplication, postChatMessage, Status } from '@/api/application';
|
||||
import { ApplicationData, approveApplication, denyApplication, loadApplication, postApplication, postChatMessage, Status } from '@/api/application';
|
||||
import { useRoute } from 'vue-router';
|
||||
import Button from '@/components/ui/button/Button.vue';
|
||||
import { CheckIcon, XIcon } from 'lucide-vue-next';
|
||||
|
||||
const appData = ref<ApplicationData>(null);
|
||||
const appID = ref<number | null>(null);
|
||||
@@ -59,6 +61,14 @@ async function postApp(appData) {
|
||||
// TODO: Handle fail to post
|
||||
}
|
||||
|
||||
async function handleApprove(id) {
|
||||
console.log("hi");
|
||||
await approveApplication(id);
|
||||
}
|
||||
|
||||
async function handleDeny(id) {
|
||||
await denyApplication(id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -90,12 +100,20 @@ async function postApp(appData) {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
}) }}</p>
|
||||
<div class="mt-2" v-else>
|
||||
<Button variant="success" class="mr-2" :onclick="() => {handleApprove(appID)}">
|
||||
<CheckIcon></CheckIcon>
|
||||
</Button>
|
||||
<Button variant="destructive" :onClick="() => {handleDeny(appID)}">
|
||||
<XIcon></XIcon>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex flex-row justify-between items-center py-2 mb-8">
|
||||
<h3 class="scroll-m-20 text-2xl font-semibold tracking-tight">Apply to join the 17th Rangers</h3>
|
||||
</div>
|
||||
<ApplicationForm :read-only="readOnly" :data="appData" @submit="(e) => {postApp(e)}" class="mb-7">
|
||||
<ApplicationForm :read-only="readOnly" :data="appData" @submit="(e) => { postApp(e) }" class="mb-7">
|
||||
</ApplicationForm>
|
||||
<div v-if="!newApp">
|
||||
<h3 class="scroll-m-20 text-2xl font-semibold tracking-tight mb-4">Discussion</h3>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import Button from '@/components/ui/button/Button.vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { CheckIcon, XIcon } from 'lucide-vue-next';
|
||||
|
||||
const appList = ref([]);
|
||||
const now = Date.now();
|
||||
@@ -86,8 +87,8 @@ onMounted(async () => {
|
||||
{{ formatAgo(app.submitted_at) }}
|
||||
</TableCell>
|
||||
<TableCell v-if="app.app_status === Status.Pending" class="inline-flex items-end gap-2">
|
||||
<Button variant="success" @click.stop="() => { console.log('hello') }">Accept</Button>
|
||||
<Button variant="destructive" @click.stop="() => { handleDeny(app.id) }">Deny</Button>
|
||||
<Button variant="success" @click.stop="() => { handleApprove(app.id) }"><CheckIcon></CheckIcon></Button>
|
||||
<Button variant="destructive" @click.stop="() => { handleDeny(app.id) }"><XIcon></XIcon></Button>
|
||||
</TableCell>
|
||||
|
||||
<TableCell class="text-right font-semibold" :class="[
|
||||
|
||||
39
ui/src/pages/RankChange.vue
Normal file
39
ui/src/pages/RankChange.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import { Check, Search } from "lucide-vue-next"
|
||||
import { Combobox, ComboboxAnchor, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList } from "@/components/ui/combobox"
|
||||
|
||||
const frameworks = [
|
||||
{ value: "next.js", label: "Next.js" },
|
||||
{ value: "sveltekit", label: "SvelteKit" },
|
||||
{ value: "nuxt", label: "Nuxt" },
|
||||
{ value: "remix", label: "Remix" },
|
||||
{ value: "astro", label: "Astro" },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex w-full items-center justify-center mt-20">
|
||||
<Combobox>
|
||||
<ComboboxAnchor class="w-[300px]">
|
||||
<ComboboxInput placeholder="Search framework..." class="w-full pl-9" />
|
||||
<Search class="absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
||||
</ComboboxAnchor>
|
||||
<ComboboxList class="w-[300px]">
|
||||
<ComboboxEmpty class="text-muted-foreground">No results</ComboboxEmpty>
|
||||
<ComboboxGroup>
|
||||
<template v-for="framework in frameworks" :key="framework.value">
|
||||
<ComboboxItem
|
||||
:value="framework.value"
|
||||
class="data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative cursor-pointer select-none px-2 py-1.5"
|
||||
>
|
||||
{{ framework.label }}
|
||||
<ComboboxItemIndicator class="absolute left-2 inline-flex items-center">
|
||||
<Check class="h-4 w-4" />
|
||||
</ComboboxItemIndicator>
|
||||
</ComboboxItem>
|
||||
</template>
|
||||
</ComboboxGroup>
|
||||
</ComboboxList>
|
||||
</Combobox>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,12 +1,14 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import ManageApplications from '@/pages/ManageApplications.vue'
|
||||
import Application from '@/pages/Application.vue'
|
||||
import RankChange from '@/pages/RankChange.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{ path: '/applications', component: ManageApplications },
|
||||
{ path: '/applications/:id', component: Application },
|
||||
{ path: '/changeRank', component: RankChange },
|
||||
]
|
||||
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user