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

Creators/image #32

Merged
merged 6 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .env.development
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
NUXT_PUBLIC_APP_URL=http://localhost:3001/
NUXT_PUBLIC_SITE_URL=https://menthor.io/
NUXT_PUBLIC_API_URL=http://localhost:3000/dev
#NUXT_PUBLIC_API_URL=http://localhost:3000/dev
NUXT_PUBLIC_API_URL=https://2vzfiy8py1.execute-api.sa-east-1.amazonaws.com/dev
NUXT_PUBLIC_UMAMI_HOST="https://analytics.umami.is/script.js"
NUXT_PUBLIC_UMAMI_ID="3ff16063-e916-4083-ad5e-66dcec525c16"
NUXT_PUBLIC_APP_UMAMI_ID="8746ccb0-1a39-489a-8c8f-a394e4e2ee3b"
Expand Down
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [menthorlabs]
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"typescript.tsdk": "node_modules/typescript/lib",
"cSpell.language": "en,pt,pt-BR",
"cSpell.enabledLanguageIds": ["javascript", "typescript"],
"cSpell.words": ["nuxt", "menthor", "ofetch", "menthorlabs", "sakura", "pinia"],
"cSpell.words": ["nuxt", "menthor", "ofetch", "menthorlabs", "sakura", "pinia", "colorthief"],
}
58 changes: 58 additions & 0 deletions apps/app/components/atoms/CreatorsImageCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script setup lang="ts">
import { useClipboard } from "@vueuse/core";
const { fileUrl = "" } = defineProps<{
fileUrl: string;
}>();

const source = ref(fileUrl);
const { copy, copied } = useClipboard({ source });
</script>

<template>
<div
v-if="fileUrl === 'loading'"
class="flex items-center justify-center w-full aspect-video bg-zinc-100 animate-pulse"
>
<svg
class="w-10 h-10 text-zinc-200"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 18"
>
<path
d="M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z"
/>
</svg>
</div>
<div v-else class="relative group">
<div
class="absolute top-2 right-2 z-20 hidden group-hover:flex items-center gap-1"
>
<nuxt-link external target="_blank" :to="fileUrl">
<MIconButton icon="external-link-alt" class="!text-white" />
</nuxt-link>
<MIconButton icon="times" class="!text-white" />
</div>

<div
class="absolute z-10 top-0 left-0 w-full h-full items-center justify-center bg-black/50 cursor-pointer hidden group-hover:flex"
@click="copy()"
>
<span class="font-semibold text-sm text-white">
{{ copied ? "Copiado!" : "Copiar url" }}
</span>
</div>
<div class="aspect-video w-full overflow-hidden bg-zinc-100">
<nuxt-img
:src="fileUrl"
:quality="85"
format="webp"
width="250"
height="140"
alt="Roadmap"
class="h-full w-full object-cover object-center transition-all group-hover:scale-110"
/>
</div>
</div>
</template>
18 changes: 18 additions & 0 deletions apps/app/components/molecules/LeftMenuCreators.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<div class="w-full flex-1 p-3">
<div class="text-zinc-700 font-medium pl-4 py-[10px] text-sm">
Criação de conteúdo
</div>
<nuxt-link
to="/creators/images"
class="group"
exact-active-class="is-active"
>
<LeftMenuItem
icon="image"
text="Imagens"
class="group-[.is-active]:text-zinc-950 group-[.is-active]:bg-zinc-100"
/>
</nuxt-link>
</div>
</template>
19 changes: 13 additions & 6 deletions apps/app/components/molecules/TaskModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ async function sendSubmission(status: "Pending" | "Draft") {
if (!currentLesson) return;

const payload: typeof submissionsStore.submission = {
Filename: submissionsStore.submission?.Filename || "",
Content: submissionsStore.submission?.Content || "",
SubmissionType: currentLesson.submissionContent,
SubmissionStatus: status,
Expand All @@ -62,9 +63,11 @@ async function sendSubmission(status: "Pending" | "Draft") {
submissionsStore.submission?.SubmissionType === "Image" &&
uploadedFile.value
) {
await submissionsStore.requestUrl(uploadedFile.value.type);
await submissionsStore.uploadFileOnUrl(uploadedFile.value);
submissionsStore.submission.Content = `https://menthor-lessons.s3.sa-east-1.amazonaws.com/${submissionsStore.uploadUrl?.fileName}`;
const response = await submissionsStore.requestUrl(
uploadedFile.value.type
);
await submissionsStore.uploadFileOnUrl(uploadedFile.value, response.url);
submissionsStore.submission.Filename = response.fileName;
await submissionsStore.updateSubmission();
}
coursesStore.updateCourseLessons(currentLesson._id);
Expand All @@ -90,7 +93,7 @@ async function uploadFile(file: File) {

if (!submissionsStore.submission) return;
uploadedFile.value = file;
submissionsStore.submission.Content = URL.createObjectURL(file);
submissionsStore.submission.Filename = URL.createObjectURL(file);
}
</script>

Expand Down Expand Up @@ -119,9 +122,13 @@ async function uploadFile(file: File) {
class="mb-4"
/>
<nuxt-img
v-if="submissionsStore.submission.Content"
v-if="submissionsStore.submission.Filename"
class="rounded overflow-hidden"
:src="submissionsStore.submission.Content"
:src="
submissionsStore.submission.Filename.includes('blob')
? submissionsStore.submission.Filename
: `https://menthor-lessons.s3.sa-east-1.amazonaws.com/${submissionsStore.submission.Filename}`
"
alt="Submission Image"
/>
</template>
Expand Down
7 changes: 7 additions & 0 deletions apps/app/components/molecules/TopBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const shadowGenerator = (color: Array<Color>): string => {
<template>
<div
class="absolute left-0 top-0 flex h-[90px] w-full justify-center overflow-hidden pointer-events-none"
v-if="shadowStore.primaryColors && shadowStore.secondaryColors"
>
<div class="relative h-full w-full max-w-[1017px]">
<div
Expand Down Expand Up @@ -66,6 +67,12 @@ const shadowGenerator = (color: Array<Color>): string => {
<NuxtLink to="/profile">
<DropdownItem icon="circle-user" name="Perfil" />
</NuxtLink>
<NuxtLink
to="/creators/images"
v-if="userStore.user?.publicMetadata?.isCreator"
>
<DropdownItem icon="star" name="Creators" />
</NuxtLink>
<NuxtLink to="/sign-out">
<DropdownItem icon="arrow-right-from-bracket" name="Sair" />
</NuxtLink>
Expand Down
11 changes: 10 additions & 1 deletion apps/app/components/organisms/DefaultAside.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const isLesson = computed(() => {
return !!route.meta?.lesson;
});

const isCreators = computed(() => {
return route.path.includes("/creators");
});

watch(isLesson, async () => {
refresh();
});
Expand Down Expand Up @@ -66,7 +70,12 @@ watch(isLesson, async () => {
</div>
</Transition>
<Transition name="slide-fade-reverse">
<div v-if="!isLesson">
<div v-if="!isLesson && isCreators">
<LeftMenuCreators />
</div>
</Transition>
<Transition name="slide-fade-reverse">
<div v-if="!isLesson && !isCreators">
<LeftMenuDefault />
</div>
</Transition>
Expand Down
102 changes: 102 additions & 0 deletions apps/app/pages/creators/images.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<script setup lang="ts">
const creatorsStore = useCreatorsStore();
const uploadStore = useUploadStore();
const loading = ref(true);

onMounted(async () => {
await creatorsStore.getImages();
loading.value = false;
});

function getSizePercentage(bytes: number) {
const tenGigabytes = 10737418240;

return Number((bytes * 100) / tenGigabytes).toFixed(2);
}

function onInput(event: Event) {
const allFiles = (event.target as HTMLInputElement).files;

if (!allFiles) return;

for (let i = 0; i < allFiles.length; ++i) {
const file = allFiles[i];
uploadFile(file);
}
}

async function uploadFile(file: File) {
loading.value = true;
const response = await creatorsStore.signUrl(file.type);
await uploadStore.uploadFileOnUrl(file, response.signedUrl.url);
await creatorsStore.addImage(response.signedUrl.fileName);
loading.value = false;
}
</script>

<template>
<div
class="container pt-4"
:class="{ 'overflow-hidden h-[calc(100%_-_56px)]': loading }"
>
<h1 class="text-4xl font-extrabold mb-4">Suas imagens</h1>
<div class="flex items-center gap-4 mb-8">
<MButton
variant="outline"
text="Fazer upload"
icon-left="cloud-arrow-up"
@click="($refs['creators_image'] as HTMLInputElement).click()"
/>
<input
ref="creators_image"
id="creators_image"
name="creators_image"
type="file"
accept="image/*"
multiple
class="hidden"
@input="onInput"
/>

<div class="space-y-[4px] w-full max-w-[240px] pt-2">
<div class="rounded-full bg-zinc-200 h-[4px]">
<div
class="w-full h-full rounded-full bg-emerald-500"
:style="`max-width: ${getSizePercentage(
creatorsStore.filesSize
)}%;`"
></div>
</div>
<div class="flex text-zinc-500 text-sm">
<div class="flex-1">
Usado:
<span class="text-zinc-950 font-medium">{{
$filters.bytesToSize(creatorsStore.filesSize)
}}</span>
de 10 GB
</div>
<div>{{ getSizePercentage(creatorsStore.filesSize) }} %</div>
</div>
</div>
</div>
<div
class="grid grid-cols-[repeat(auto-fill,_minmax(200px,_1fr))] gap-1 relative"
v-if="loading"
>
<CreatorsImageCard v-for="i in 30" :key="i" fileUrl="loading" />
<div
class="absolute top-0 left-0 w-full h-full bg-[linear-gradient(180deg,_rgba(255,_255,_255,_0.00)_0%,_#FFF_100%)] z-10"
></div>
</div>
<div
class="grid grid-cols-[repeat(auto-fill,_minmax(200px,_1fr))] gap-1"
v-else
>
<CreatorsImageCard
v-for="fileName in creatorsStore.images.reverse()"
:key="fileName"
:fileUrl="`https://menthor-content.s3.sa-east-1.amazonaws.com/${fileName}`"
/>
</div>
</div>
</template>
2 changes: 1 addition & 1 deletion apps/app/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ useSeoMeta({
</div>
<h2 class="mb-4 text-lg font-bold">Recomendados para você</h2> -->
<div
class="mb-6 grid grid-cols-[repeat(auto-fill,_minmax(160px,_1fr))] sm:grid-cols-[repeat(auto-fill,_minmax(180px,_1fr))]"
class="mb-6 grid grid-cols-[repeat(auto-fill,_minmax(160px,_1fr))] sm:grid-cols-[repeat(auto-fill,_minmax(180px,_1fr))] gap-4 sm:gap-0"
>
<ContentNavigation v-slot="{ navigation }">
<NuxtLink
Expand Down
10 changes: 10 additions & 0 deletions apps/app/plugins/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ export default defineNuxtPlugin((nuxtApp) => {
};
return levels[value];
},
bytesToSize(bytes: number): string {
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
if (bytes === 0) return "n/a";
const i = Math.min(
Math.floor(Math.log(bytes) / Math.log(1024)),
sizes.length - 1
);
if (i === 0) return `${bytes} ${sizes[i]}`;
return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}`;
},
};

app.config.globalProperties.$filters = filters;
Expand Down
4 changes: 4 additions & 0 deletions apps/app/plugins/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import {
faCheck,
faTerminal,
faCircleInfo,
faExternalLinkAlt,
faMoon,
faSun,
faEdit,
faHashtag,
faStar,
faLock,
faImage,
faClipboard,
faArrowRightFromBracket,
faArrowUp,
Expand Down Expand Up @@ -48,12 +50,14 @@ library.add(
faCheck,
faTerminal,
faCircleInfo,
faExternalLinkAlt,
faMoon,
faSun,
faEdit,
faHashtag,
faStar,
faLock,
faImage,
faClipboard,
faArrowRightFromBracket,
faArrowUp,
Expand Down
Loading