after audit
This commit is contained in:
parent
e8e20637e6
commit
a5165dc3ba
80 changed files with 8489 additions and 7302 deletions
|
|
@ -0,0 +1,113 @@
|
|||
<script setup lang="ts">
|
||||
import AppButton from "@/components/ui/AppButton.vue";
|
||||
import AppInput from "@/components/ui/AppInput.vue";
|
||||
import AppModal from "@/components/ui/AppModal.vue";
|
||||
import ScholarAvatar from "@/features/scholars/components/ScholarAvatar.vue";
|
||||
import type { ScholarProfile } from "@/features/scholars";
|
||||
|
||||
const props = defineProps<{
|
||||
scholar: ScholarProfile | null;
|
||||
imageUrlDraft: string;
|
||||
imageBusy: boolean;
|
||||
imageSaving: boolean;
|
||||
imageUploading: boolean;
|
||||
saving: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "close"): void;
|
||||
(e: "update:image-url-draft", value: string): void;
|
||||
(e: "save-image-url"): void;
|
||||
(e: "upload-image", event: Event): void;
|
||||
(e: "reset-image"): void;
|
||||
(e: "toggle"): void;
|
||||
(e: "delete"): void;
|
||||
}>();
|
||||
|
||||
function scholarLabel(profile: ScholarProfile): string {
|
||||
return profile.display_name || profile.scholar_id;
|
||||
}
|
||||
|
||||
function scholarProfileUrl(scholarId: string): string {
|
||||
return `https://scholar.google.com/citations?hl=en&user=${encodeURIComponent(scholarId)}`;
|
||||
}
|
||||
|
||||
function scholarPublicationsRoute(profile: ScholarProfile): { name: string; query: { scholar: string } } {
|
||||
return { name: "publications", query: { scholar: String(profile.id) } };
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppModal :open="scholar !== null" title="Scholar settings" @close="emit('close')">
|
||||
<div v-if="scholar" class="grid gap-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<ScholarAvatar
|
||||
:label="scholar.display_name"
|
||||
:scholar-id="scholar.scholar_id"
|
||||
:image-url="scholar.profile_image_url"
|
||||
/>
|
||||
<div class="min-w-0 space-y-1">
|
||||
<p class="truncate text-sm font-semibold text-ink-primary">{{ scholarLabel(scholar) }}</p>
|
||||
<p class="text-xs text-secondary">ID: <code>{{ scholar.scholar_id }}</code></p>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<RouterLink :to="scholarPublicationsRoute(scholar)" class="link-inline text-xs">
|
||||
View publications
|
||||
</RouterLink>
|
||||
<a :href="scholarProfileUrl(scholar.scholar_id)" target="_blank" rel="noreferrer" class="link-inline text-xs">
|
||||
Open Google Scholar profile
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2">
|
||||
<label class="text-sm font-medium text-ink-secondary" :for="`scholar-image-url-${scholar.id}`">
|
||||
Profile image URL override
|
||||
</label>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
<AppInput
|
||||
:id="`scholar-image-url-${scholar.id}`"
|
||||
:model-value="imageUrlDraft"
|
||||
placeholder="https://example.com/avatar.jpg"
|
||||
:disabled="imageBusy"
|
||||
@update:model-value="emit('update:image-url-draft', $event as string)"
|
||||
/>
|
||||
</div>
|
||||
<AppButton variant="secondary" :disabled="imageBusy" @click="emit('save-image-url')">
|
||||
{{ imageSaving ? "Saving..." : "Save URL" }}
|
||||
</AppButton>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label
|
||||
:for="`scholar-image-upload-${scholar.id}`"
|
||||
class="inline-flex min-h-10 cursor-pointer items-center justify-center rounded-lg border border-stroke-strong bg-action-secondary-bg px-3 py-2 text-sm font-semibold text-action-secondary-text transition hover:bg-action-secondary-hover-bg focus-within:outline-none focus-within:ring-2 focus-within:ring-focus-ring focus-within:ring-offset-2 focus-within:ring-offset-focus-offset"
|
||||
:class="{ 'pointer-events-none opacity-60': imageBusy }"
|
||||
>
|
||||
{{ imageUploading ? "Uploading..." : "Upload image" }}
|
||||
</label>
|
||||
<input
|
||||
:id="`scholar-image-upload-${scholar.id}`"
|
||||
type="file"
|
||||
class="sr-only"
|
||||
accept="image/jpeg,image/png,image/webp,image/gif"
|
||||
:disabled="imageBusy"
|
||||
@change="emit('upload-image', $event)"
|
||||
/>
|
||||
<AppButton variant="ghost" :disabled="imageBusy" @click="emit('reset-image')">
|
||||
Reset image
|
||||
</AppButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-between gap-2 border-t border-stroke-default pt-3">
|
||||
<AppButton variant="secondary" :disabled="imageBusy || saving" @click="emit('toggle')">
|
||||
{{ scholar.is_enabled ? "Disable scholar" : "Enable scholar" }}
|
||||
</AppButton>
|
||||
<AppButton variant="danger" :disabled="imageBusy || saving" @click="emit('delete')">
|
||||
Delete scholar
|
||||
</AppButton>
|
||||
</div>
|
||||
</div>
|
||||
</AppModal>
|
||||
</template>
|
||||
Loading…
Add table
Add a link
Reference in a new issue