CLASSROOMIO-T-11:Finished the certificate issuing page

This commit is contained in:
emmanuel
2023-07-24 12:10:14 +01:00
parent a263c4303b
commit 71c200763a
14 changed files with 636 additions and 0 deletions
@@ -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

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB