CLASSROOMIO-T-11:Finished the certificate issuing page
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
<script>
|
||||
import {
|
||||
RadioButtonGroup,
|
||||
RadioButton,
|
||||
Toggle,
|
||||
} from 'carbon-components-svelte';
|
||||
import UploadImage from './Upload.svelte';
|
||||
import PrimaryButton from '../../../PrimaryButton/index.svelte';
|
||||
import TextArea from '../../../Form/TextArea.svelte';
|
||||
import Introductorycert from './certificates/Introductorycert.svelte';
|
||||
import Maincert from './certificates/Maincert.svelte';
|
||||
import { certInfo } from './store';
|
||||
|
||||
const theme1 = './theme1.png';
|
||||
const theme2 = './theme2.png';
|
||||
|
||||
const themes = [
|
||||
{ label: 'theme1', src: theme1 },
|
||||
{ label: 'theme2', src: theme2 },
|
||||
];
|
||||
|
||||
let Selectedtheme = themes[0];
|
||||
const saveCertificate = () => {};
|
||||
const onToggle = (e) => {
|
||||
console.log(e);
|
||||
};
|
||||
</script>
|
||||
|
||||
<main class="md:-ml-4 md:-mr-4">
|
||||
<div
|
||||
class="flex-1 flex flex-col md:flex-row justify-between gap-3 w-full mb-3 h-4/5"
|
||||
>
|
||||
<section class="w-full md:w-2/5 h-full">
|
||||
<strong class="my-2 text-base font-semibold text-black"
|
||||
>Certificate settings</strong
|
||||
>
|
||||
<p class="text-xs font-normal my-4">Choose a theme</p>
|
||||
<RadioButtonGroup bind:selected={Selectedtheme} class="mb-10">
|
||||
{#each themes as value}
|
||||
<div class="flex mr-3">
|
||||
<RadioButton {value} size="sm" />
|
||||
<img src={value.src} alt="themes" />
|
||||
</div>
|
||||
{/each}
|
||||
</RadioButtonGroup>
|
||||
<div>
|
||||
<p class="text-xs font-normal text-black my-2">Brand Logo</p>
|
||||
<p class="text-base font-normal mt-2 mb-4">
|
||||
We recommend using a PNG file with transparency. Maximum size: 1200 x
|
||||
600px.
|
||||
</p>
|
||||
|
||||
<UploadImage />
|
||||
|
||||
<span class="my-4">
|
||||
<p class="text-xs font-normal mt-4 mb-2">Course Description</p>
|
||||
<TextArea
|
||||
maxRows={6}
|
||||
rows={6}
|
||||
placeholder="a little description about the course"
|
||||
bgColor="bg-gray-100"
|
||||
bind:value={$certInfo.Desc}
|
||||
onChange={$certInfo.Desc}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<Toggle
|
||||
labelText="Add date of completion"
|
||||
on:toggle={(e) => onToggle(e)}
|
||||
class="my-2"
|
||||
size="sm"
|
||||
>
|
||||
<span slot="labelA" style="color: #161616">Automatic</span>
|
||||
<span slot="labelB" style="color: green">Automatic</span>
|
||||
</Toggle>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
class="certbg flex justify-center items-center rounded-md w-full md:w-3/5"
|
||||
>
|
||||
<div class="flex justify-center items-center h-5/6 w-5/6">
|
||||
{#if Selectedtheme.label == 'theme1'}
|
||||
<Introductorycert />
|
||||
{:else}
|
||||
<Maincert />
|
||||
{/if}
|
||||
<!-- <img src="./certImg.png" alt="cert" class="object-cover" /> -->
|
||||
<!-- <img src={Selectedtheme.src} alt="cert" class="img object-cover" /> -->
|
||||
<!-- {Selectedtheme.label} -->
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="h-1/5">
|
||||
<PrimaryButton
|
||||
label="Save Changes"
|
||||
className="rounded-md"
|
||||
onClick={() => saveCertificate()}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
p {
|
||||
color: #262626;
|
||||
}
|
||||
.certbg {
|
||||
background-color: #f5f8fe;
|
||||
}
|
||||
/* .img {
|
||||
width: 600px;
|
||||
height: 200px;
|
||||
} */
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<script>
|
||||
import { Toggle } from 'carbon-components-svelte';
|
||||
import Modal from '../../../Modal/index.svelte';
|
||||
import PrimaryButton from '../../../PrimaryButton/index.svelte';
|
||||
import TextArea from '../../../Form/TextArea.svelte';
|
||||
import TextField from '../../../Form/TextField.svelte';
|
||||
import { issueCertModal, resetForm } from './store';
|
||||
|
||||
const onToggle = (e) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
const issueCert = () => {
|
||||
resetForm();
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
onClose={resetForm}
|
||||
bind:open={$issueCertModal.open}
|
||||
width="w-3/5"
|
||||
maxWidth=""
|
||||
modalHeading="Send Certificate"
|
||||
>
|
||||
<main>
|
||||
<div>
|
||||
<Toggle
|
||||
labelText="Automatically send certificates on completion"
|
||||
on:toggle={(e) => onToggle(e)}
|
||||
size="sm"
|
||||
class="mb-4"
|
||||
>
|
||||
<span slot="labelA" style="color: #161616">Automatic</span>
|
||||
<span slot="labelB" style="color: green">Automatic</span>
|
||||
</Toggle>
|
||||
<p class="text-sm font-medium my-4">
|
||||
If you set this as automatic, certificates will be issued after the
|
||||
learner completes the course.
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-xs font-normal text-gray-500 my-4">
|
||||
or send a personalised/ custom certificate below:
|
||||
</p>
|
||||
<form on:submit|preventDefault={issueCert}>
|
||||
<div class="flex flex-col md:flex-row gap-2 w-full">
|
||||
<TextField
|
||||
label="Email address of the student"
|
||||
className="w-full my-4"
|
||||
labelClassName="text-xs font-normal"
|
||||
inputClassName="text-sm placeholder:text-sm placeholder:font-medium "
|
||||
placeholder="email,comma seperated"
|
||||
bind:value={$issueCertModal.email}
|
||||
/>
|
||||
<TextField
|
||||
label="Schedule date"
|
||||
className="w-full my-4"
|
||||
labelClassName="text-xs font-normal"
|
||||
inputClassName="text-sm placeholder:text-sm placeholder:font-medium "
|
||||
placeholder="12/06/2023"
|
||||
bind:value={$issueCertModal.date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TextArea
|
||||
label="Add a personalized message"
|
||||
labelClassName="text-xs font-normal"
|
||||
bind:value={$issueCertModal.message}
|
||||
rows="2"
|
||||
maxRows="3"
|
||||
placeholder="your message here"
|
||||
className="mb-4"
|
||||
/>
|
||||
|
||||
<div class="mt-5 flex w-full items-end justify-end">
|
||||
<PrimaryButton
|
||||
className="px-6 py-3 rounded-md text-sm font-medium"
|
||||
label="Issue certificate"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</Modal>
|
||||
@@ -0,0 +1,99 @@
|
||||
<script>
|
||||
import { CertData, students } from './mockData';
|
||||
import {
|
||||
Search,
|
||||
Checkbox,
|
||||
StructuredList,
|
||||
StructuredListHead,
|
||||
StructuredListRow,
|
||||
StructuredListCell,
|
||||
StructuredListBody,
|
||||
Pagination,
|
||||
} from 'carbon-components-svelte';
|
||||
|
||||
let searchValue = '';
|
||||
let checked = false;
|
||||
</script>
|
||||
|
||||
<section class="flex flex-wrap items-start gap-2 mb-10 -ml-4 -mr-4">
|
||||
{#each CertData as data (data.id)}
|
||||
<div
|
||||
class="box flex flex-col rounded border border-gray-200 justify-center px-2 mb-5"
|
||||
>
|
||||
<p class="text-xs font-normal">{data.title}</p>
|
||||
<bold class="text-2xl font-medium">{data.value}</bold>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<p class="text-base font-semibold w-full">Certificate issued</p>
|
||||
<Search
|
||||
placeholder="search students"
|
||||
bind:value={searchValue}
|
||||
searchClass="border-b border-transparent"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mt-14 border-t border-gray-300">
|
||||
<StructuredList>
|
||||
<StructuredListHead class="bg-blue-50 py-1">
|
||||
<StructuredListRow head>
|
||||
<StructuredListCell head>
|
||||
<div
|
||||
class="flex-3 flex flex-row items-center justify-start w-full text-blue-600 text-sm font-medium"
|
||||
>
|
||||
<Checkbox bind:checked />
|
||||
<p class="text-blue-600 text-start w-full text-sm font-medium">
|
||||
Name of student
|
||||
</p>
|
||||
</div>
|
||||
</StructuredListCell>
|
||||
<StructuredListCell head>
|
||||
<div
|
||||
class="flex-2 items-center w-full text-blue-600 text-sm font-medium"
|
||||
>
|
||||
<p class="align-middle text-sm font-medium pt-1">Date issued</p>
|
||||
<p class="opacity-0">personalized</p>
|
||||
</div>
|
||||
</StructuredListCell>
|
||||
</StructuredListRow>
|
||||
</StructuredListHead>
|
||||
<StructuredListBody>
|
||||
{#each students as item (item.id)}
|
||||
<StructuredListRow label for="row-{item}">
|
||||
<StructuredListCell>
|
||||
<div
|
||||
class="flex-3 flex flex-row items-center justify-start w-full text-sm font-medium pt-4"
|
||||
>
|
||||
<Checkbox />
|
||||
<p class="text-start w-full text-sm font-normal">{item.name}</p>
|
||||
</div>
|
||||
</StructuredListCell>
|
||||
<StructuredListCell>
|
||||
<div class="flex-2 flex flex-row items-center gap-10 w-full">
|
||||
<p class="text-sm font-normal align-middle">{item.issued}</p>
|
||||
{#if item.peronalized == true}
|
||||
<span class="text-center bg-blue-100 px-3 py-2 rounded-lg">
|
||||
<p class="opacity-100 text-xs text-blue-600">personalized</p>
|
||||
</span>
|
||||
{:else}
|
||||
<p class="opacity-0">personalized</p>
|
||||
{/if}
|
||||
</div>
|
||||
</StructuredListCell>
|
||||
</StructuredListRow>
|
||||
{/each}
|
||||
</StructuredListBody>
|
||||
</StructuredList>
|
||||
<Pagination totalItems={10} pageSizes={[10, 15, 20]} />
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.box {
|
||||
width: 246px;
|
||||
height: 165px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<script>
|
||||
import Camera from 'carbon-icons-svelte/lib/Camera20';
|
||||
|
||||
export let avatar = null;
|
||||
export let src = null;
|
||||
|
||||
let fileinput;
|
||||
|
||||
const onFileSelected = (e) => {
|
||||
const image = e.target.files[0];
|
||||
let reader = new FileReader();
|
||||
reader.readAsDataURL(image);
|
||||
reader.onload = (e) => {
|
||||
avatar = image;
|
||||
src = e.target.result;
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="width-fit flex flex-col items-center">
|
||||
<p
|
||||
class="width-fit text-blue-700 flex items-center cursor-pointer text-sm"
|
||||
on:click={() => {
|
||||
fileinput.click();
|
||||
}}
|
||||
>
|
||||
<Camera /> <span class="ml-2">Upload Image</span>
|
||||
</p>
|
||||
|
||||
<input
|
||||
style="display:none"
|
||||
type="file"
|
||||
accept=".jpg, .jpeg, .png"
|
||||
on:change={(e) => onFileSelected(e)}
|
||||
bind:this={fileinput}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.width-fit {
|
||||
width: fit-content;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<script>
|
||||
import TextField from '../../../../Form/TextField.svelte';
|
||||
import TextArea from '../../../../Form/TextArea.svelte';
|
||||
import { certInfo } from '../store';
|
||||
|
||||
let borderwidth = 'border-l-8';
|
||||
let bordercolor = 'border-blue-800';
|
||||
let logo = './certLogo.png';
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-center border border-dashed border-blue-400"
|
||||
>
|
||||
<div class="{borderwidth} {bordercolor}">
|
||||
<div class="w-full px-5 py-3">
|
||||
<p class="text-sm font-normal my-2 uppercase text-gray-500">
|
||||
Certificate<br /> of completion
|
||||
</p>
|
||||
<div class="border-b border-gray-500 mb-4">
|
||||
<p class="text-xs text-black font-medium">This is to certify that</p>
|
||||
<TextField
|
||||
bind:value={$certInfo.Name}
|
||||
inputClassName="text-center placeholder:text-black"
|
||||
placeholder="Name of student"
|
||||
bgColor="bg-transparent"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<p class="text-xs text-black font-medium">
|
||||
has successfully completed introductory courses in
|
||||
</p>
|
||||
<TextArea
|
||||
bind:value={$certInfo.Title}
|
||||
inputClassName=""
|
||||
placeholder="Name of course"
|
||||
bgColor="clamp bg-transparent font-semibold text-blue-800 text-center placeholder:text-black placeholder:text-base placeholder:font-semibold uppercase resize-none border-none"
|
||||
rows="1"
|
||||
maxRows="2"
|
||||
/>
|
||||
</div>
|
||||
<p class="desc mb-2">{$certInfo.Desc}</p>
|
||||
<div>
|
||||
<img src={logo} alt="logo" class="my-2" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.desc {
|
||||
font-size: 10px;
|
||||
}
|
||||
.clamp {
|
||||
font-size: clamp(14px, 3vw, 16px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<script>
|
||||
import TextField from '../../../../Form/TextField.svelte';
|
||||
import TextArea from '../../../../Form/TextArea.svelte';
|
||||
import { certInfo } from '../store';
|
||||
|
||||
let borderwidth = 'border-4';
|
||||
let bordercolor = 'border-pink-700';
|
||||
let logo = './certLogo.png';
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="w-full flex flex-col items-center justify-center {borderwidth} {bordercolor}"
|
||||
>
|
||||
<div>
|
||||
<img src={logo} alt="logo" />
|
||||
</div>
|
||||
<div class="w-full px-5 py-3">
|
||||
<p
|
||||
class="text-sm text-center font-normal my-2 uppercase tracking-wider text-gray-500 w-full"
|
||||
>
|
||||
Certificate of completion
|
||||
</p>
|
||||
<div class="border-b border-gray-500 mb-4">
|
||||
<p class="text-xs text-black font-medium">This is to certify that</p>
|
||||
<TextField
|
||||
bind:value={$certInfo.Name}
|
||||
inputClassName="text-center placeholder:text-black"
|
||||
placeholder="Name of student"
|
||||
bgColor="bg-transparent"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<p class="text-xs text-black font-medium">
|
||||
has successfully completed a courses in
|
||||
</p>
|
||||
<TextArea
|
||||
bind:value={$certInfo.Title}
|
||||
inputClassName=""
|
||||
placeholder="Name of course"
|
||||
bgColor="clamp bg-transparent text-base font-semibold text-pink-700 text-center placeholder:text-black placeholder:text-base placeholder:font-semibold uppercase resize-none"
|
||||
rows="1"
|
||||
maxRows="2"
|
||||
/>
|
||||
</div>
|
||||
<p class="desc text-center">{$certInfo.Desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.desc {
|
||||
font-size: 10px;
|
||||
}
|
||||
.clamp {
|
||||
font-size: clamp(14px, 3vw, 16px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,85 @@
|
||||
export const CertData = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Total students taking course',
|
||||
value: 100,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Number of certificates issued ',
|
||||
value: 45,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Certificates left to be issued',
|
||||
value: 55,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Personalised cetificates',
|
||||
value: 15,
|
||||
},
|
||||
];
|
||||
|
||||
export const students = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: false,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: false,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: false,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: false,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: false,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: false,
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Anyanwu joy victory',
|
||||
issued: '10 Dec 2020',
|
||||
peronalized: true,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,28 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
// export const deleteMemberModal = writable({
|
||||
// open: false,
|
||||
// });
|
||||
|
||||
export const issueCertModal = writable({
|
||||
open: false,
|
||||
automatic: false,
|
||||
email: '',
|
||||
date: '',
|
||||
message: '',
|
||||
});
|
||||
|
||||
export const certInfo = writable({
|
||||
Name: 'Name of student',
|
||||
Title: 'Desiging functional components',
|
||||
Desc: '(Introduction to Digital Devices, The Internet Mobile Applications, Introduction to Social Media, Using social media for business, Digital Content Creation and Distribution, Using mobile money in business, Digital financial management,Digital Empowerment,Digital Safety)',
|
||||
});
|
||||
|
||||
export const resetForm = () =>
|
||||
issueCertModal.update(() => ({
|
||||
open: false,
|
||||
automatic: false,
|
||||
email: '',
|
||||
date: '',
|
||||
message: '',
|
||||
}));
|
||||
@@ -125,6 +125,11 @@
|
||||
to: getNavItemRoute($course.id, 'people'),
|
||||
hideSortIcon: true,
|
||||
},
|
||||
{
|
||||
label: 'Certificates',
|
||||
to: getNavItemRoute($course.id, 'certificates'),
|
||||
hideSortIcon: true,
|
||||
},
|
||||
{
|
||||
label: 'Landing Page',
|
||||
to: getNavItemRoute($course.id, 'landingpage'),
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<script context="module">
|
||||
import {
|
||||
fetchCourse,
|
||||
} from '../../../utils/services/courses';
|
||||
|
||||
export async function preload({ params }) {
|
||||
return { courseId: params.id };
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import CourseContainer from '../../../components/CourseContainer/index.svelte';
|
||||
|
||||
import PageNav from '../../../components/PageNav/index.svelte';
|
||||
import PrimaryButton from '../../../components/PrimaryButton/index.svelte';
|
||||
import PageBody from '../../../components/PageBody/index.svelte';
|
||||
import RoleBasedSecurity from '../../../components/RoleBasedSecurity/index.svelte';
|
||||
import { setCourse, course, group } from '../../../components/Course/store';
|
||||
import IssueCertModal from '../../../components/Course/components/Ceritficate/IssueCertModal.svelte';
|
||||
// import DeleteConfirmation from '../../../components/Course/components/People/DeleteConfirmation.svelte';
|
||||
import Design from '../../../components/Course/components/Ceritficate/Design.svelte';
|
||||
import Reports from '../../../components/Course/components/Ceritficate/Reports.svelte';
|
||||
|
||||
import { issueCertModal } from '../../../components/Course/components/Ceritficate/store';
|
||||
import { Tabs, Tab, TabContent } from 'carbon-components-svelte';
|
||||
|
||||
export let courseId: string = '';
|
||||
let isStudent = false;
|
||||
onMount(async () => {
|
||||
if ($course.id) return;
|
||||
const { data } = await fetchCourse(courseId);
|
||||
setCourse(data);
|
||||
});
|
||||
</script>
|
||||
|
||||
<IssueCertModal />
|
||||
<CourseContainer bind:isStudent>
|
||||
<PageNav title="Certificates" disableSticky={true}>
|
||||
<slot:fragment slot="widget">
|
||||
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
||||
<PrimaryButton
|
||||
className="mr-2 rounded-md text-sm font-medium"
|
||||
label="Issue Cert"
|
||||
onClick={() => ($issueCertModal.open = true)}
|
||||
/>
|
||||
</RoleBasedSecurity>
|
||||
</slot:fragment>
|
||||
</PageNav>
|
||||
<PageBody className="w-full mx-0" padding="px-0">
|
||||
<Tabs autoWidth class="border-b border-gray-200">
|
||||
<Tab label="Design" />
|
||||
<Tab label="Reports" />
|
||||
<svelte:fragment slot="content">
|
||||
<TabContent>
|
||||
<Design />
|
||||
</TabContent>
|
||||
|
||||
<TabContent>
|
||||
<Reports />
|
||||
</TabContent>
|
||||
</svelte:fragment>
|
||||
</Tabs>
|
||||
</PageBody>
|
||||
</CourseContainer>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Reference in New Issue
Block a user