add 3 demo calendar

This commit is contained in:
schooltechx 2023-03-27 16:43:56 +07:00
parent da46b61ad7
commit 466ecf5981
20 changed files with 765 additions and 33 deletions

View file

@ -1,9 +1,9 @@
version: "3.4"
services:
hi:
qualifying-exam-cms:
image: docker.frappet.com/demo/qualifying-exam-cms:latest
restart: unless-stopped
ports:
- "4010:80"
environment:
TZ: Asia/Bangkok
TZ: Asia/Bangkok

46
cms/package-lock.json generated
View file

@ -18,6 +18,7 @@
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.7",
"dayjs": "^1.11.7",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte3": "^4.0.0",
@ -28,6 +29,7 @@
"prettier-plugin-svelte": "^2.8.1",
"sass": "^1.58.3",
"svelte": "^3.54.0",
"svelte-calendar": "^3.1.6",
"svelte-check": "^3.0.1",
"svelte-preprocess": "^4.10.7",
"svelte-seo": "^1.5.3",
@ -1553,6 +1555,12 @@
"postcss": "^8.1.6"
}
},
"node_modules/dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==",
"dev": true
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -2514,6 +2522,12 @@
"npm": ">=6"
}
},
"node_modules/just-throttle": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/just-throttle/-/just-throttle-2.3.1.tgz",
"integrity": "sha512-0H4miIAWZYpnpg7oD/Y/PBb77ISSHAETif5xK9EnwIgYCO6oC8ErkJxDumMUTR44shSOwptRIArRuvNuvN/hOw==",
"dev": true
},
"node_modules/jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
@ -3563,6 +3577,16 @@
"node": ">= 8"
}
},
"node_modules/svelte-calendar": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/svelte-calendar/-/svelte-calendar-3.1.6.tgz",
"integrity": "sha512-jOHiPlxBAa1LGVFQZoczdAGnCSI2RKcuQQHj32TjLbXO/P2Povx4JphPEcP7XjKj/s/jimBC6xQyFhyjD0vxbg==",
"dev": true,
"dependencies": {
"dayjs": "^1.10.6",
"just-throttle": "^2.3.1"
}
},
"node_modules/svelte-check": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.1.0.tgz",
@ -5160,6 +5184,12 @@
"tailwindcss": "^3"
}
},
"dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==",
"dev": true
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -5882,6 +5912,12 @@
"semver": "^7.3.8"
}
},
"just-throttle": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/just-throttle/-/just-throttle-2.3.1.tgz",
"integrity": "sha512-0H4miIAWZYpnpg7oD/Y/PBb77ISSHAETif5xK9EnwIgYCO6oC8ErkJxDumMUTR44shSOwptRIArRuvNuvN/hOw==",
"dev": true
},
"jwa": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
@ -6575,6 +6611,16 @@
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
"dev": true
},
"svelte-calendar": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/svelte-calendar/-/svelte-calendar-3.1.6.tgz",
"integrity": "sha512-jOHiPlxBAa1LGVFQZoczdAGnCSI2RKcuQQHj32TjLbXO/P2Povx4JphPEcP7XjKj/s/jimBC6xQyFhyjD0vxbg==",
"dev": true,
"requires": {
"dayjs": "^1.10.6",
"just-throttle": "^2.3.1"
}
},
"svelte-check": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.1.0.tgz",

View file

@ -21,6 +21,7 @@
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.7",
"dayjs": "^1.11.7",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte3": "^4.0.0",
@ -31,6 +32,7 @@
"prettier-plugin-svelte": "^2.8.1",
"sass": "^1.58.3",
"svelte": "^3.54.0",
"svelte-calendar": "^3.1.6",
"svelte-check": "^3.0.1",
"svelte-preprocess": "^4.10.7",
"svelte-seo": "^1.5.3",

View file

@ -0,0 +1,131 @@
<script lang="ts">
import {createEventDispatcher, onMount} from 'svelte'
let dispatch = createEventDispatcher();
import calendarize from './calendarize'
import Arrow from './CalendarArrow.svelte';
export let year = 2023;
export let month = 0; // Jan
export let offset = 0; // Sun
export let locale = "en" as (keyof typeof text_labels)|(keyof typeof text_months);
export let buddhist = false
const text_labels = {
en: ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'],
th: ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส'],
}
const text_months = {
en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
th: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
}
const today = new Date
const labels = (locale in text_labels)? text_labels[locale]:text_labels.en
const months = (locale in text_months)? text_months[locale]:text_months.en
$: today_month = today.getMonth();
$: today_year = today.getFullYear();
$: today_day = today.getDate();
let prev = calendarize(new Date(year, month-1), offset);
let current = calendarize(new Date(year, month), offset);
let next = calendarize(new Date(year, month+1), offset);
function toPrev() {
[current, next] = [prev, current];
if (--month < 0) {
month = 11;
year--;
}
prev = calendarize(new Date(year, month-1), offset);
}
function toNext() {
[prev, current] = [current, next];
if (++month > 11) {
month = 0;
year++;
}
next = calendarize(new Date(year, month+1), offset);
}
function isToday(day:number ) {
return today && today_year === year && today_month === month && today_day === day;
}
</script>
<header>
<Arrow left on:click={toPrev} />
<h4>{months[month]} - {year+ (buddhist?543:0)}</h4>
<Arrow on:click={toNext} />
</header>
<div class="month">
{#each labels as txt, idx (txt)}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span class="label" on:click={()=>dispatch('headerClick',txt)} >{ labels[(idx + offset) % 7] }</span>
{/each}
{#each { length:6 } as w,idxw (idxw)}
{#if current[idxw]}
{#each { length:7 } as d,idxd (idxd)}
{#if current[idxw][idxd] != 0}
<span class="date" class:today={isToday(current[idxw][idxd])}>
{ current[idxw][idxd] }
</span>
{:else if (idxw < 1)}
<span class="date other">{ prev[prev.length - 1][idxd] }</span>
{:else}
<span class="date other">{ next[0][idxd] }</span>
{/if}
{/each}
{/if}
{/each}
</div>
<style>
header {
display: flex;
margin: 2rem auto;
align-items: center;
justify-content: center;
user-select: none;
}
h4 {
display: block;
text-align: center;
text-transform: uppercase;
font-size: 140%;
margin: 0 1rem;
}
.month {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: right;
grid-gap: 4px;
}
.label {
font-weight: 300;
text-align: center;
text-transform: uppercase;
margin-bottom: 0.5rem;
opacity: 0.6;
}
.date {
height: 50px;
font-size: 16px;
letter-spacing: -1px;
border: 1px solid #e6e4e4;
padding-right: 4px;
font-weight: 700;
padding: 0.5rem;
}
.date.today {
color: #5286fa;
background: #c4d9fd;
border-color: currentColor;
}
.date.other {
opacity: 0.2;
}
</style>

View file

@ -0,0 +1,22 @@
<script lang="ts">
export let left = false;
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<svg class:left on:click xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="#c4d9fd" d="M0 256c0 141.2 114.8 256 256 256V0A256.3 256.3 0 000 256z"/>
<path fill="#c4d9fd" d="M256 0v512c141.2 0 256-114.8 256-256S397.2 0 256 0z"/>
<path fill="#5286fa" d="M226 115.4a23.3 23.3 0 00-33 33L300.7 256 193 363.7a23.3 23.3 0 1033 32.9l124-124.1a23.3 23.3 0 000-33l-124-124z"/></svg>
<style>
svg {
width: 3rem;
height: 3rem;
padding: 12px;
cursor: pointer;
}
.left {
transform: rotateY(180deg);
}
</style>

View file

@ -0,0 +1,22 @@
//Modify by Oom
//code from https://www.npmjs.com/package/calendarize
export default function (target?: Date|string, offset=0) {
let i=0, j=0, week = Array(7)
const out=[], date = new Date(target || new Date);
const year = date.getFullYear(), month = date.getMonth();
// day index (of week) for 1st of month
let first = new Date(year, month, 1 - (offset | 0)).getDay();
// how many days there are in this month
const days = new Date(year, month+1, 0).getDate();
while (i < days) {
for (j=0, week=Array(7); j < 7;) {
while (j < first) week[j++] = 0;
week[j++] = ++i > days ? 0 : i;
first = 0;
}
out.push(week);
}
return out;
}

View file

@ -0,0 +1,191 @@
<script lang="ts">
import Calendar from "./CalendarContent.svelte";
export let locale = "en" as (keyof typeof text_labels)|(keyof typeof text_months);
export let buddhist = false
const text_labels = {
en: ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'],
th: ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส'],
}
const text_months = {
en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
th: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
}
const labels = (locale in text_labels)? text_labels[locale]:text_labels.en
const months = (locale in text_months)? text_months[locale]:text_months.en
let headers: string[] = [];
let now = new Date();
let year = now.getFullYear(); // this is the month & year displayed
let month = now.getMonth();
let eventText="Click an item or date";
var days: { name:string,enabled:boolean,date: Date }[] = []; // The days to display in each box
function randInt(max: number) {
return Math.floor(Math.random()*max)+1;
}
// The Calendar Component just displays stuff in a row & column. It has no knowledge of dates.
// The items[] below are placed (by you) in a specified row & column of the calendar.
// You need to call findRowCol() to calc the row/col based on each items start date. Each date box has a Date() property.
// And, if an item overlaps rows, then you need to add a 2nd item on the subsequent row.
var items: (
{ title: string; className: string; date: Date; len: number; isBottom?: boolean;
detailHeader?: string; detailContent?: string; vlen?: number; startCol?:number;startRow?:number}
)[] = [];
function initMonthItems() {
let y = year;
let m = month;
let d1=new Date(y,m,randInt(7)+7);
items=[
{title:"11:00 Task Early in month",className:"task--primary",date:new Date(y,m,randInt(6)),len:randInt(4)+1},
{title:"7:30 Wk 2 tasks",className:"task--warning",date:d1,len:randInt(4)+2},
{title:"Overlapping Stuff (isBottom:true)",date:d1,className:"task--info",len:4,isBottom:true},
{title:"10:00 More Stuff to do",date:new Date(y,m,randInt(7)+14),className:"task--info",len:randInt(4)+1,detailHeader:"Difficult",detailContent:"But not especially so"},
{title:"All day task",date:new Date(y,m,randInt(7)+21),className:"task--danger",len:1,vlen:2},
];
//This is where you calc the row/col to put each dated item
for (let i of items) {
let rc = findRowCol(i.date);
if (rc == null) {
console.log('didn`t find date for ',i);
console.log(i.date);
console.log(days);
i.startCol = i.startRow = 0;
} else {
i.startCol = rc.col;
i.startRow = rc.row;
}
}
}
$: month,year,initContent();
// choose what date/day gets displayed in each date box.
function initContent() {
headers = labels;
initMonth();
initMonthItems();
}
function initMonth() {
days = [];
let monthAbbrev = months[month].slice(0,3);
let nextMonthAbbrev = months[(month+1)%12].slice(0,3);
// find the last Monday of the previous month
var firstDay = new Date(year, month, 1).getDay();
//console.log('fd='+firstDay+' '+dayNames[firstDay]);
var daysInThisMonth = new Date(year, month+1, 0).getDate();
var daysInLastMonth = new Date(year, month, 0).getDate();
var prevMonth = month==0 ? 11 : month-1;
// show the days before the start of this month (disabled) - always less than 7
for (let i=daysInLastMonth-firstDay;i<daysInLastMonth;i++) {
let d = new Date(prevMonth==11?year-1:year,prevMonth,i+1);
days.push({name:''+(i+1),enabled:false,date:d,});
}
// show the days in this month (enabled) - always 28 - 31
for (let i=0;i<daysInThisMonth;i++) {
let d = new Date(year,month,i+1);
if (i==0) days.push({name:monthAbbrev+' '+(i+1),enabled:true,date:d,});
else days.push({name:''+(i+1),enabled:true,date:d,});
//console.log('i='+i+' dt is '+d+' date() is '+d.getDate());
}
// show any days to fill up the last row (disabled) - always less than 7
for (let i=0;days.length%7;i++) {
let d = new Date((month==11?year+1:year),(month+1)%12,i+1);
if (i==0) days.push({name:nextMonthAbbrev+' '+(i+1),enabled:false,date:d,});
else days.push({name:''+(i+1),enabled:false,date:d,});
}
}
function findRowCol(dt:Date) {
for (let i=0;i<days.length;i++) {
let d = days[i].date;
if (d.getFullYear() === dt.getFullYear()
&& d.getMonth() === dt.getMonth()
&& d.getDate() === dt.getDate())
return {row:Math.floor(i/7)+2,col:i%7+1};
}
return null;
}
function itemClick(e: { date: { toString: () => string; }; }) {
eventText='itemClick '+JSON.stringify(e) + ' localtime='+e.date.toString();
}
function dayClick(e: { date: { toString: () => string; }; }) {
eventText='onDayClick '+JSON.stringify(e) + ' localtime='+e.date.toString();
}
function headerClick(e: any) {
eventText='onHheaderClick '+JSON.stringify(e);
}
function next() {
month++;
if (month == 12) {
year++;
month=0;
}
}
function prev() {
if (month==0) {
month=11;
year--;
} else {
month--;
}
}
</script>
<div class="calendar-container">
<div class="calendar-header">
<h1>
<button on:click={()=>year--}>&Lt;</button>
<button on:click={()=>prev()}>&lt;</button>
{months[month]} {year+ (buddhist?543:0)}
<button on:click={()=>next()}>&gt;</button>
<button on:click={()=>year++}>&Gt;</button>
</h1>
{eventText}
</div>
<Calendar
{headers}
{days}
{items}
on:dayClick={(e)=>dayClick(e.detail)}
on:itemClick={(e)=>itemClick(e.detail)}
on:headerClick={(e)=>headerClick(e.detail)}
/>
</div>
<style>
.calendar-container {
width: 100%;
margin: auto;
overflow: hidden;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
border-radius: 10px;
background: #fff;
max-width: 1200px;
}
.calendar-header {
text-align: center;
padding: 20px 0;
background: #eef;
border-bottom: 1px solid rgba(166, 168, 179, 0.12);
}
.calendar-header h1 {
margin: 0;
font-size: 18px;
}
.calendar-header button {
background: #eef;
border: 1px ;
padding: 6;
color: rgba(81, 86, 93, 0.7);
cursor: pointer;
outline: 0;
}
</style>

View file

@ -0,0 +1,222 @@
<div class="calendar">
{#each headers as header}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span class="day-name" on:click={()=>dispatch('headerClick',header)}>{header}</span>
{/each}
{#each days as day}
{#if day.enabled}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span class="day" on:click={()=>dispatch('dayClick',day)}>{day.name}</span>
{:else}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span class="day day-disabled" on:click={()=>dispatch('dayClick',day)}>{day.name}</span>
{/if}
{/each}
{#each items as item}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<section
on:click={()=>dispatch('itemClick',item)}
class="task {item.className}"
style="grid-column: {item.startCol} / span {item.len};
grid-row: {item.startRow};
align-self: {item.isBottom?'end':'center'};"
>
{item.title}
{#if item.detailHeader}
<div class="task-detail">
<h2>{item.detailHeader}</h2>
<p>{item.detailContent}</p>
</div>
{/if}
</section>
{/each}
</div>
<script>
import {createEventDispatcher, onMount} from 'svelte';
/**
* @type {any[]}
*/
export var headers = [];
/**
* @type {any[]}
*/
export let days = [];
/**
* @type {any[]}
*/
export let items = [];
let dispatch = createEventDispatcher();
</script>
<style>
.calendar {
display: grid;
width: 100%;
grid-template-columns: repeat(7, minmax(120px, 1fr));
grid-template-rows: 50px;
grid-auto-rows: 120px;
overflow: auto;
}
.day {
border-bottom: 1px solid rgba(166, 168, 179, 0.12);
border-right: 1px solid rgba(166, 168, 179, 0.12);
text-align: right;
padding: 14px 20px;
letter-spacing: 1px;
font-size: 14px;
box-sizing: border-box;
color: #98a0a6;
position: relative;
z-index: 1;
}
.day:nth-of-type(7n + 7) {
border-right: 0;
}
.day:nth-of-type(n + 1):nth-of-type(-n + 7) {
grid-row: 1;
}
.day:nth-of-type(n + 8):nth-of-type(-n + 14) {
grid-row: 2;
}
.day:nth-of-type(n + 15):nth-of-type(-n + 21) {
grid-row: 3;
}
.day:nth-of-type(n + 22):nth-of-type(-n + 28) {
grid-row: 4;
}
.day:nth-of-type(n + 29):nth-of-type(-n + 35) {
grid-row: 5;
}
.day:nth-of-type(n + 36):nth-of-type(-n + 42) {
grid-row: 6;
}
.day:nth-of-type(7n + 1) {
grid-column: 1/1;
}
.day:nth-of-type(7n + 2) {
grid-column: 2/2;
}
.day:nth-of-type(7n + 3) {
grid-column: 3/3;
}
.day:nth-of-type(7n + 4) {
grid-column: 4/4;
}
.day:nth-of-type(7n + 5) {
grid-column: 5/5;
}
.day:nth-of-type(7n + 6) {
grid-column: 6/6;
}
.day:nth-of-type(7n + 7) {
grid-column: 7/7;
}
.day-name {
font-size: 12px;
text-transform: uppercase;
color: #e9a1a7;
text-align: center;
border-bottom: 1px solid rgba(166, 168, 179, 0.12);
line-height: 50px;
font-weight: 500;
}
.day-disabled {
color: rgba(152, 160, 166, 0.5);
background-color: #ffffff;
background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fdf9ff' fill-opacity='1' fill-rule='evenodd'%3E%3Cpath d='M0 40L40 0H20L0 20M40 40V20L20 40'/%3E%3C/g%3E%3C/svg%3E");
cursor: not-allowed;
}
.task {
border-left-width: 3px;
padding: 8px 12px;
margin: 10px;
border-left-style: solid;
font-size: 14px;
position: relative;
align-self: center;
z-index:2;
border-radius: 15px;
}
.task--warning {
border-left-color: #fdb44d;
background: #fef0db;
color: #fc9b10;
margin-top: -5px;
}
.task--danger {
border-left-color: #fa607e;
grid-column: 2 / span 3;
grid-row: 3;
margin-top: 15px;
background: rgba(253, 197, 208, 0.7);
color: #f8254e;
}
.task--info {
border-left-color: #4786ff;
margin-top: 15px;
background: rgba(218, 231, 255, 0.7);
color: #0a5eff;
}
.task--primary {
background: #4786ff;
border: 0;
border-radius: 14px;
color: #f00;
box-shadow: 0 10px 14px rgba(71, 134, 255, 0.4);
}
.task-detail {
position: absolute;
left: 0;
top: calc(100% + 8px);
background: #efe;
border: 1px solid rgba(166, 168, 179, 0.2);
color: #100;
padding: 20px;
box-sizing: border-box;
border-radius: 14px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
z-index: 2;
}
.task-detail:after, .task-detail:before {
bottom: 100%;
left: 30%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.task-detail:before {
border-bottom-color: rgba(166, 168, 179, 0.2);
border-width: 8px;
margin-left: -8px;
}
.task-detail:after {
border-bottom-color: #fff;
border-width: 6px;
margin-left: -6px;
}
.task-detail h2 {
font-size: 15px;
margin: 0;
color: #91565d;
}
.task-detail p {
margin-top: 4px;
font-size: 12px;
margin-bottom: 0;
font-weight: 500;
color: rgba(81, 86, 93, 0.7);
}
</style>

View file

@ -0,0 +1,5 @@
[
{"id":20,"date":"2023/03/25","detail":"ประกาศรับสมัครสอบและคัดเลือกบุคคลเข้ารับราชการเป็นลูกจ้างชั่วคราว","institute":"สำนักอนามัย","time":"๕/๒๕๖๕"},
{"id":19,"date":"2023/03/23","detail":"ประกาศรายชื่อผู้มีสิทธิเข้ารับการสอบคัดเลือกบุคคลภายนอกช่วยปฎิบัติราชการ","institute":"สำนักการแพทย์","time":"๔/๒๕๖๔"},
{"id":18,"date":"2023/03/20","detail":"ประกาศกรุงเทพมหานคร เรื่อง ประกาศผู้ชนะการเสนอราคา จ้างเหมาซ่อมแซมป้ายชื่อ","institute":"สำนักวัฒนธรรม กีฬาและการท่องเที่ยว","time":"๔/๒๕๖๓"}
]

View file

@ -0,0 +1,4 @@
import exams from "./qualify-exam.json"
export async function getExams(){
return exams
}

View file

@ -4,7 +4,7 @@
</script>
<Header />
<div class="p-10 max-w-4xl place-content-center ">
<div class="p-5 max-w-4xl place-content-center ">
<div>
<slot />
</div>

View file

@ -7,5 +7,5 @@ export const load: PageServerLoad = async () => {
}*/
export const load = (async () => {
return getContact()
return await getContact()
}) satisfies PageServerLoad

View file

@ -1,15 +1,54 @@
<script lang="ts">
import type { PageData } from './$types'
import 'dayjs/locale/th.js';
import 'dayjs/locale/zh-cn.js';
import 'dayjs/locale/es.js';
import 'dayjs/locale/ar-dz.js';
// มันรองรับ พ.ศ. ใน dayjs แต่ใน velte-calendar ไม่ได้เอาไปใช้ ยังหาวิธีแก้หัวปฎิทินไม่ได้ยังเป็น พ.ศ.
import buddhistEra from 'dayjs/plugin/buddhistEra'
import dayjs from 'dayjs';
dayjs.extend(buddhistEra)
import { InlineCalendar, Swappable, themes } from 'svelte-calendar';
import { onDestroy } from 'svelte';
const locales = ['th','en', 'es', 'zh-cn', 'ar-dz'];
$: dayjs.locale(locale);
onDestroy(() => dayjs.locale('en'));
import Calendar2 from '$lib/components/Calendar2/Calendar.svelte';
import Calendar from '$lib/components/Calendar/Calendar.svelte';
const today = new Date
const locale = "th"
const buddhist = true //buddhist calendar + 543
let year = today.getFullYear()
let month = today.getMonth()
import ver from "$lib/ver.json"
import type { PageData } from './$types'
export let data: PageData;
</script>
<h1 class="text-4xl">{data.company_name}</h1>
<h1 class="text-4xl">เกี่ยวกับ {data.company_name}</h1>
<div>{data.description}</div>
<div>{ver.version}</div>
<div>{ver.builddate}</div>
<h2 class="text-2xl">svelte-calendar</h2>
<p>3rd svelte-calendar ไม่รองรับ พ.ศ.</p>
<!-- note: Locale is not reactive -->
<Swappable value={{ locale }} vertical={false} >
<InlineCalendar theme={themes.light} />
</Swappable>
<h2 class="text-2xl">Custom Calendar1</h2>
<div>
Calendar ทำเองยังไม่รองรับการแสดง event ในวัน
</div>
<Calendar {year} {month} {locale} {buddhist}/>
<h2 class="text-2xl">Custom Calendar2</h2>
<div>
ทำเองรองรับการแสดง event และคลิ้กได้ ได้แต่ยังปรับโค้ดไม่เรียบร้อย
</div>
<Calendar2 {locale} {buddhist} />

View file

@ -1,7 +1,35 @@
<h1 class="text-4xl">การสอบแข่งขัน</h1>
<script lang="ts">
import 'dayjs/locale/th.js';
import 'dayjs/locale/zh-cn.js';
import 'dayjs/locale/es.js';
import 'dayjs/locale/ar-dz.js';
// มันรองรับ พ.ศ. ใน dayjs แต่ใน velte-calendar ไม่ได้เอาไปใช้ ยังหาวิธีแก้หัวปฎิทินไม่ได้ยังเป็น พ.ศ.
import buddhistEra from 'dayjs/plugin/buddhistEra'
import dayjs from 'dayjs';
dayjs.extend(buddhistEra)
import { InlineCalendar, Swappable, themes } from 'svelte-calendar';
import { onDestroy } from 'svelte';
fdsafda
fdsafda sdaf
sdaffsdasaf rfffffffffffffffff fffffffffffffffffffffffff ffffffffffff fgedfgfdgfdsg
fedsddddddddddddd ddddddddddddddd ddddddddd d d d d d d d d d d d d xxxx
const locales = ['th','en', 'es', 'zh-cn', 'ar-dz'];
let format = 'DD/MM/BBBB'
let locale = 'th';
$: dayjs.locale(locale);
onDestroy(() => dayjs.locale('en'));
</script>
<h1 class="text-4xl">การสอบแข่งขัน</h1>
<!-- note: Locale is not reactive -->
<Swappable value={{ locale }} vertical={false} >
<InlineCalendar theme={themes.light} {format} />
</Swappable>
<div class="button-group">
{#each locales as loc}
<button on:click={() => (locale = loc)} class:selected={locale === loc}>
{loc}
</button> |
{/each}
</div>

View file

@ -0,0 +1,8 @@
import {getExams} from "$lib/data/qualify-exam"
import type { PageServerLoad } from './$types'
export const load: PageServerLoad = async () => {
const exams = await getExams()
return {exams}
}

View file

@ -1,6 +1,28 @@
<script lang="ts">
import type { PageData } from './$types'
export let data: PageData;
const exams = data.exams
</script>
<h1 class="text-4xl">การสอบคัดเลือก</h1>
<button class="btn btn-outline">Button</button>
<button class="btn btn-outline btn-primary">Button</button>
<button class="btn btn-outline btn-secondary">Button</button>
<h1 class="text-2xl">รายการสอบ</h1>
<div class="overflow-x-auto">
<table class="table table-compact w-full">
<thead>
<tr>
<th>วันที่</th>
<th>การสอบ</th>
<th>หน่วยงาน</th>
</tr>
</thead>
<tbody>
{#each exams as exam}
<tr>
<th><a href={"/exams/"+exam.id}>{exam.date}</a></th>
<td>{exam.detail}</td>
<td>{exam.institute}</td>
{/each}
</tbody>
</table>
</div>

View file

@ -15,6 +15,7 @@ const config = {
],
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.

View file

@ -6,4 +6,5 @@ export default defineConfig({
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
}
});