1.2 Next.js Building a Car Trader App #1- Introduction and FAQ Page (ok)

https://www.npmjs.com/package/better-sqlite3

C:\Users\Administrator\Desktop\nextjs\package.json

{
  "name": "nextjs",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "@material-ui/core": "^4.11.3",
    "@material-ui/icons": "^4.11.2",
    "@material-ui/lab": "^4.0.0-alpha.57",
    "axios": "^0.21.1",
    "formik": "^2.2.6",
    "next": "10.0.8",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "sqlite": "^4.0.19",
    "sqlite3": "^5.0.2",
    "swr": "^0.5.2"
  },
  "devDependencies": {
    "@types/node": "^14.14.33",
    "@types/react": "^17.0.3",
    "typescript": "^4.2.3"
  }
}

C:\Users\Administrator\Desktop\pro\database-test.js

const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
async function setup() {
  const db = await sqlite.open({
    filename: 'cars.sqlite',
    driver: sqlite3.Database,
  });
  await db.migrate({ force: 'last' });
  const faq = await db.all('SELECT * FROM FAQ ORDER BY createDate DESC');
  console.log('ALL faq', JSON.stringify(faq, null, 2));
  const cars = await db.all('SELECT * FROM Car');
  console.log('ALL CARS', JSON.stringify(cars, null, 2));
}
setup();

Hoặc viết theo lối hiện đại hơn

const sqlite = require('sqlite');
const sqlite3 = require('sqlite3');
(async () => {
  const db = await sqlite.open({
    filename: 'cars.sqlite',
    driver: sqlite3.Database,
  });
  await db.migrate({ force: 'last' });
  const faq = await db.all('SELECT * FROM FAQ ORDER BY createDate DESC');
  console.log('ALL faq', JSON.stringify(faq, null, 2));
  const cars = await db.all('SELECT * FROM Car');
  console.log('ALL CARS', JSON.stringify(cars, null, 2));
})()

1. Create FAQ

C:\Users\Administrator\Desktop\nextjs\pages\index.tsx

export default function Home() {
  return <h1>Hello Youtube</h1>;
}

C:\Users\Administrator\Desktop\nextjs\pages\faq.tsx

import { Accordion, AccordionDetails, AccordionSummary, Typography } from '@material-ui/core';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { GetStaticProps } from 'next';
import  FaqModel  from '../api/Faq';
import  openDB  from '../openDB';
interface FaqProps {
  faq: FaqModel[];
}
export default function Faq({ faq }: FaqProps) {
  return (
    <div>
      {faq.map((f) => (
        <Accordion key={f.id}>
          <AccordionSummary
            expandIcon={<ExpandMoreIcon />}
            aria-controls="panel1a-content"
            id="panel1a-header"
          >
            <Typography>
              {f.question}
            </Typography>
          </AccordionSummary>
          <AccordionDetails>
            <Typography>
              {f.answer}
            </Typography>
          </AccordionDetails>
        </Accordion>
      ))}
    </div>
  );
}
export const getStaticProps: GetStaticProps = async () => {
  const db = await openDB();
  const faq = await db.all('SELECT * FROM FAQ ORDER BY createDate DESC');
  return { props: { faq } };
};

C:\Users\Administrator\Desktop\nextjs\interface\Car.ts

export interface CarModel {
  id: number;
  make: string;
  model: string;
  year: number;
  fuelType: string;
  kilometers: number;
  details: string;
  price: number;
  photoUrl: string;
}

C:\Users\Administrator\Desktop\nextjs\api\Faq.ts

export interface FaqModel {
    id: number;
    question: string;
    answer: string;
}

C:\Users\Administrator\Desktop\nextjs\components\Nav.tsx

import { AppBar, Button, makeStyles, Toolbar, Typography } from '@material-ui/core';
import Link from 'next/link';
import React from 'react';
const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1
  },
  menuButton: {
    marginRight: theme.spacing(2)
  },
  title: {
    flexGrow: 1
  }
}));
export function Nav() {
  const classes = useStyles();
  return (
    <AppBar position="static">
      <Toolbar variant="dense">
        <Typography variant="h6" className={classes.title}>
          Car Trader
        </Typography>
        <Button color="inherit">
          <Link href="/">
            <a style={{ color: 'white' }}>
              <Typography  color="inherit">
                Home
              </Typography>
            </a>
          </Link>
        </Button>
        <Button color="inherit">
          <Link href="/faq">
            <a style={{ color: 'white' }}>
              <Typography  color="inherit">
                FAQ
              </Typography>
            </a>
          </Link>
        </Button>
      </Toolbar>
    </AppBar>
  );
}

Source

Viết theo cách mới

pages\faq.tsx

import { GetStaticProps } from 'next';
import openDB from './openDB';
function toggleAccordion(index: any) {
  const content = document.getElementById(`content-${index}`);
  const icon = document.getElementById(`icon-${index}`);
  const minusSVG = `
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
      <path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
    </svg>
  `;
  const plusSVG = `
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
      <path d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" />
    </svg>
  `;
  if (content!.style.maxHeight && content!.style.maxHeight !== '0px') {
    content!.style.maxHeight = '0';
    icon!.innerHTML = plusSVG;
  } else {
    content!.style.maxHeight = content?.scrollHeight + 'px';
    icon!.innerHTML = minusSVG;
  }
}
export default function Faq({ faq }: any) {
  return (
    <div className="w-screen p-3">
      {faq.map((f: any) => (
        <div key={f.id} className="border-b border-slate-200" onClick={() => toggleAccordion(f.id)}>
          <button className="w-full flex justify-between items-center py-5 text-slate-800">
            <span>{f.question}</span>
            <span id={`icon-${f.id}`} className="text-slate-800 transition-transform duration-300">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" className="w-4 h-4">
                <path fillRule="evenodd" d="M11.78 9.78a.75.75 0 0 1-1.06 0L8 7.06 5.28 9.78a.75.75 0 0 1-1.06-1.06l3.25-3.25a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06Z" clipRule="evenodd" />
              </svg>
            </span>
          </button>
          <div id={`content-${f.id}`} className="max-h-0 overflow-hidden transition-all duration-300 ease-in-out">
            <div className="pb-5 text-sm text-slate-500">
              {f.answer}
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}
export const getStaticProps: GetStaticProps = async () => {
  const db = await openDB();
  const faq = await db.all('SELECT * FROM FAQ ORDER BY createDate DESC');
  return { props: { faq } };
};

pages\openDB.ts

import { open } from 'sqlite'
import sqlite3 from 'sqlite3'
// you would have to import / invoke this in another file
export default async function openDB () {
  return open({
    filename: './pages/data/helloworld.sql',
    driver: sqlite3.Database
  })
}

Last updated

Was this helpful?