├── public ├── robots.txt ├── manifest.json └── favicon.svg ├── .prettierignore ├── src ├── components │ ├── header │ │ ├── header.css │ │ └── header.tsx │ ├── hello-message │ │ ├── hello-message.css │ │ └── hello-message.tsx │ └── router-head │ │ └── router-head.tsx ├── models │ ├── lesson.ts │ └── course.ts ├── global.css ├── routes │ ├── layout.tsx │ ├── head-links.tsx │ ├── service-worker.ts │ ├── index.tsx │ ├── stores │ │ └── index.tsx │ └── resources │ │ └── index.tsx ├── entry.dev.tsx ├── entry.preview.tsx ├── entry.ssr.tsx └── root.tsx ├── backend ├── server.tsconfig.json ├── delete-course.route.ts ├── get-courses.route.ts ├── server.ts ├── search.route.ts └── db-data.ts ├── vite.config.ts ├── .eslintignore ├── .gitignore ├── tsconfig.json ├── .eslintrc.cjs ├── package.json └── README.md /public/robots.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Files Prettier should not format 2 | **/*.log 3 | **/.DS_Store 4 | *. 5 | dist 6 | node_modules 7 | -------------------------------------------------------------------------------- /src/components/header/header.css: -------------------------------------------------------------------------------- 1 | header { 2 | display: flex; 3 | } 4 | 5 | header ul { 6 | list-style: none; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /backend/server.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": ["es2017"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/models/lesson.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface Lesson { 3 | id: number; 4 | description: string; 5 | duration: string; 6 | seqNo: number; 7 | courseId: number; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/hello-message/hello-message.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .hello-message { 4 | font-size: 30px; 5 | font-weight: bold; 6 | color:blue; 7 | } 8 | 9 | .hello-message.highlighted { 10 | color: orange; 11 | } 12 | -------------------------------------------------------------------------------- /src/models/course.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export interface Course { 4 | id:string; 5 | description:string; 6 | iconUrl: string; 7 | courseListIcon: string; 8 | longDescription: string; 9 | category:string; 10 | lessonsCount:number; 11 | url:string; 12 | } 13 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/web-manifest-combined.json", 3 | "name": "qwik-project-name", 4 | "short_name": "Welcome to Qwik", 5 | "start_url": ".", 6 | "display": "standalone", 7 | "background_color": "#fff", 8 | "description": "A Qwik project app." 9 | } 10 | -------------------------------------------------------------------------------- /src/components/header/header.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useStylesScoped$ } from '@builder.io/qwik'; 2 | import styles from './header.css?inline'; 3 | import { Link } from "@builder.io/qwik-city"; 4 | 5 | export default component$(() => { 6 | useStylesScoped$(styles); 7 | 8 | return ( 9 |
10 | 13 |
14 | ); 15 | }); 16 | -------------------------------------------------------------------------------- /src/global.css: -------------------------------------------------------------------------------- 1 | /** 2 | * WHAT IS THIS FILE? 3 | * 4 | * Globally applied styles. No matter which components are in the page or matching route, 5 | * the styles in here will be applied to the Document, without any sort of CSS scoping. 6 | * 7 | */ 8 | 9 | /* global font */ 10 | h1, h2, h3, h3, h5, h6, span:not(.material-icons), div, ul, li, p { 11 | font-family: Inter, sans-serif; 12 | } 13 | -------------------------------------------------------------------------------- /src/routes/layout.tsx: -------------------------------------------------------------------------------- 1 | import { component$, Slot } from '@builder.io/qwik'; 2 | import Header from '../components/header/header'; 3 | 4 | export default component$(() => { 5 | return ( 6 | <> 7 |
8 |
9 |
10 | 11 |
12 |
13 | 16 | 17 | ); 18 | }); 19 | -------------------------------------------------------------------------------- /backend/delete-course.route.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import {Request, Response} from 'express'; 4 | import {COURSES} from "./db-data"; 5 | 6 | 7 | 8 | 9 | export function deleteCourseById(req: Request, res: Response) { 10 | 11 | const courseId = req.params["id"]; 12 | 13 | console.log(`Deleting course with id ${courseId}`); 14 | 15 | delete COURSES[courseId]; 16 | 17 | res.status(200).json({ status: "OK" }); 18 | } 19 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { qwikVite } from '@builder.io/qwik/optimizer'; 3 | import { qwikCity } from '@builder.io/qwik-city/vite'; 4 | import tsconfigPaths from 'vite-tsconfig-paths'; 5 | 6 | export default defineConfig(() => { 7 | return { 8 | plugins: [qwikCity(), qwikVite(), tsconfigPaths()], 9 | preview: { 10 | headers: { 11 | 'Cache-Control': 'public, max-age=600', 12 | }, 13 | }, 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | dist 30 | tsconfig.tsbuildinfo 31 | vite.config.ts 32 | *.spec.tsx 33 | *.spec.ts 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | /dist 3 | /lib 4 | /lib-types 5 | /server 6 | 7 | # Development 8 | node_modules 9 | 10 | # Cache 11 | .cache 12 | .mf 13 | .vscode 14 | .rollup.cache 15 | tsconfig.tsbuildinfo 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | pnpm-debug.log* 24 | lerna-debug.log* 25 | 26 | # Editor 27 | !.vscode/extensions.json 28 | .idea 29 | .DS_Store 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | *.sw? 35 | 36 | # Yarn 37 | .yarn/* 38 | !.yarn/releases 39 | -------------------------------------------------------------------------------- /src/routes/head-links.tsx: -------------------------------------------------------------------------------- 1 | import { DocumentLink } from "@builder.io/qwik-city"; 2 | 3 | 4 | export const commonLinks : DocumentLink[] = [ 5 | { 6 | href: "https://fonts.googleapis.com", 7 | rel: "preconnect" 8 | }, 9 | { 10 | href: "https://fonts.gstatic.com", 11 | rel: "preconnect", 12 | crossorigin: "true" 13 | }, 14 | { 15 | href:"https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap", 16 | rel: "stylesheet" 17 | }, 18 | { 19 | href:"https://fonts.googleapis.com/icon?family=Material+Icons", 20 | rel: "stylesheet" 21 | } 22 | ]; 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/entry.dev.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * Development entry point using only client-side modules: 5 | * - Do not use this mode in production! 6 | * - No SSR 7 | * - No portion of the application is pre-rendered on the server. 8 | * - All of the application is running eagerly in the browser. 9 | * - More code is transferred to the browser than in SSR mode. 10 | * - Optimizer/Serialization/Deserialization code is not exercised! 11 | */ 12 | import { render, RenderOptions } from '@builder.io/qwik'; 13 | import Root from './root'; 14 | 15 | export default function (opts: RenderOptions) { 16 | return render(document, , opts); 17 | } 18 | -------------------------------------------------------------------------------- /src/entry.preview.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * It's the bundle entry point for `npm run preview`. 5 | * That is, serving your app built in production mode. 6 | * 7 | * Feel free to modify this file, but don't remove it! 8 | * 9 | * Learn more about Vite's preview command: 10 | * - https://vitejs.dev/config/preview-options.html#preview-options 11 | * 12 | */ 13 | import { createQwikCity } from '@builder.io/qwik-city/middleware/node'; 14 | import render from './entry.ssr'; 15 | import qwikCityPlan from '@qwik-city-plan'; 16 | 17 | /** 18 | * The default export is the QwikCity adaptor used by Vite preview. 19 | */ 20 | export default createQwikCity({ render, qwikCityPlan }); 21 | -------------------------------------------------------------------------------- /backend/get-courses.route.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import {Request, Response} from 'express'; 4 | import {COURSES} from "./db-data"; 5 | 6 | 7 | 8 | export function getAllCourses(req: Request, res: Response) { 9 | 10 | // throw "Error"; 11 | 12 | setTimeout(() => { 13 | 14 | res.status(200).json(Object.values(COURSES).sort((c1:any, c2:any) => c1.seqNo - c2.seqNo)); 15 | 16 | }, 2000); 17 | 18 | 19 | } 20 | 21 | 22 | export function getCourseById(req: Request, res: Response) { 23 | 24 | const courseId = req.params["id"]; 25 | 26 | const courses:any = Object.values(COURSES); 27 | 28 | const course = courses.find(course => course.id == courseId); 29 | 30 | res.status(200).json(course); 31 | } 32 | -------------------------------------------------------------------------------- /src/routes/service-worker.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * The service-worker.ts file is used to have state of the art prefetching. 5 | * https://qwik.builder.io/qwikcity/prefetching/overview/ 6 | * 7 | * Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline. 8 | * You can also use this file to add more functionality that runs in the service worker. 9 | */ 10 | import { setupServiceWorker } from '@builder.io/qwik-city/service-worker'; 11 | 12 | setupServiceWorker(); 13 | 14 | addEventListener('install', () => self.skipWaiting()); 15 | 16 | addEventListener('activate', () => self.clients.claim()); 17 | 18 | declare const self: ServiceWorkerGlobalScope; 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "ES2017", 5 | "module": "ES2020", 6 | "lib": ["es2020", "DOM", "WebWorker", "DOM.Iterable"], 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "@builder.io/qwik", 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "resolveJsonModule": true, 12 | "moduleResolution": "node", 13 | "esModuleInterop": true, 14 | "skipLibCheck": true, 15 | "incremental": true, 16 | "isolatedModules": true, 17 | "outDir": "tmp", 18 | "noEmit": true, 19 | "types": ["node", "vite/client"], 20 | "paths": { 21 | "~/*": ["./src/*"] 22 | } 23 | }, 24 | "files": ["./.eslintrc.cjs"], 25 | "include": ["src"] 26 | } 27 | -------------------------------------------------------------------------------- /src/entry.ssr.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * WHAT IS THIS FILE? 3 | * 4 | * SSR entry point, in all cases the application is render outside the browser, this 5 | * entry point will be the common one. 6 | * 7 | * - Server (express, cloudflare...) 8 | * - npm run start 9 | * - npm run preview 10 | * - npm run build 11 | * 12 | */ 13 | import { renderToStream, RenderToStreamOptions } from '@builder.io/qwik/server'; 14 | import { manifest } from '@qwik-client-manifest'; 15 | import Root from './root'; 16 | 17 | export default function (opts: RenderToStreamOptions) { 18 | return renderToStream(, { 19 | manifest, 20 | ...opts, 21 | // Use container attributes to set attributes on the html tag. 22 | containerAttributes: { 23 | lang: 'en-us', 24 | ...opts.containerAttributes, 25 | }, 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/server.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import {Application} from "express"; 3 | import {getAllCourses, getCourseById} from "./get-courses.route"; 4 | import {searchLessons} from "./search.route"; 5 | import { deleteCourseById } from "./delete-course.route"; 6 | 7 | 8 | const app: Application = express(); 9 | 10 | const cors = require('cors'); 11 | 12 | app.use(cors({origin: true})); 13 | 14 | app.route('/').get((req, res) => { 15 | res.status(200).send(`

Server is up and running.

`); 16 | }); 17 | 18 | app.route('/api/courses').get(getAllCourses); 19 | 20 | app.route('/api/courses/:id').get(getCourseById); 21 | 22 | app.route('/api/courses/:id').delete(deleteCourseById); 23 | 24 | app.route('/api/lessons').get(searchLessons); 25 | 26 | 27 | 28 | 29 | const httpServer:any = app.listen(9000, () => { 30 | console.log("HTTP REST API Server running at http://localhost:" + httpServer.address().port); 31 | }); 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { component$, $ } from "@builder.io/qwik"; 2 | import { HelloMessage } from "~/components/hello-message/hello-message"; 3 | 4 | 5 | export default component$(() => { 6 | 7 | console.log(`Initializing Hello World component.`); 8 | 9 | const sayHello = $(() => { 10 | alert("Hello World!"); 11 | }); 12 | 13 | const messages = [ 14 | "Hello World", 15 | "Welcome to this Qwik Course", 16 | "Learn the Qwik Framework!" 17 | ]; 18 | 19 | const onShowMessageClicked = $((message:string) => alert(message)); 20 | 21 | return ( 22 | <> 23 | 24 | { messages.map((message, index) => ( 25 | 27 | )) 28 | 29 | } 30 | 31 | 32 | 33 | 34 | ); 35 | 36 | }); 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/components/router-head/router-head.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from '@builder.io/qwik'; 2 | import { useDocumentHead, useLocation } from '@builder.io/qwik-city'; 3 | 4 | /** 5 | * The RouterHead component is placed inside of the document `` element. 6 | */ 7 | export const RouterHead = component$(() => { 8 | const head = useDocumentHead(); 9 | const loc = useLocation(); 10 | 11 | return ( 12 | <> 13 | {head.title} 14 | 15 | 16 | 17 | 18 | 19 | {head.meta.map((m) => ( 20 | 21 | ))} 22 | 23 | {head.links.map((l) => ( 24 | 25 | ))} 26 | 27 | {head.styles.map((s) => ( 28 |