Skip to content

Commit

Permalink
Sample app frontend (#5931)
Browse files Browse the repository at this point in the history
* Add darkmode

* Edit README

* Add comments and readme

* Fix UI

* Update README

* Update code-samples/eventing/bookstore-sample-app/frontend/client/pages/Main.js

Co-authored-by: Leo Li <leoli@redhat.com>

* Update code-samples/eventing/bookstore-sample-app/frontend/client/components/BookDetail.js

Co-authored-by: Leo Li <leoli@redhat.com>

* Change emoji

* Update code-samples/eventing/bookstore-sample-app/frontend/client/components/Toggle.js

Co-authored-by: Leo Li <leoli@redhat.com>

* Fix UI

* Align time

---------

Co-authored-by: Leo Li <leoli@redhat.com>
  • Loading branch information
ctmphuongg and Leo6Leo committed Apr 5, 2024
1 parent bf326bb commit 791daf5
Show file tree
Hide file tree
Showing 24 changed files with 8,094 additions and 0 deletions.
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
36 changes: 36 additions & 0 deletions code-samples/eventing/bookstore-sample-app/frontend/.gitignore
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
23 changes: 23 additions & 0 deletions code-samples/eventing/bookstore-sample-app/frontend/Dockerfile
@@ -0,0 +1,23 @@
# Use a base image with Node.js LTS
FROM node:lts-alpine

# Set the working directory inside the container
WORKDIR /app

# Copy package.json and package-lock.json to the working directory
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of your application code to the working directory
COPY . .

# Build the Next.js application
RUN npm run build

# Expose the port your app runs on
EXPOSE 3000

# Define the command to run your app
CMD ["npm", "run dev"]
57 changes: 57 additions & 0 deletions code-samples/eventing/bookstore-sample-app/frontend/README.md
@@ -0,0 +1,57 @@
# Getting Started

This app use Next.js and TailwindCSS as main packages. Use this command to install all dependencies:

```bash
npm install
```

To run application, use:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

# Project Structures

- app/: Contains the main layout, page, and global styling.
- client/: Contains components and pages used in the application.
- public/images/: Contains image files.
- next-env.d.ts, next.config.mjs, package-lock.json, package.json, postcss.config.js, tailwind.config.js, tsconfig.json: Configuration files for Next.js, Tailwind CSS, and TypeScript.

# Containerize Application

This repository contains a Next.js application that utilizes next-themes and Tailwind CSS. This README file provides instructions on how to containerize the application using Docker.

## Prerequisites

- Docker installed on your machine. You can download and install Docker from [here](https://www.docker.com/get-started).

## Dockerization Steps

1. Clone this repository to your local machine.
2. Navigate to the root directory of the cloned repository.

### Building the Docker Image

Run the following command to build the Docker image:

```bash
docker build -t frontend .
```

## Running the Docker Container

Once the image is built, you can run a container using the following command:

```bash
docker run -d -p 3000:3000 frontend
```
@@ -0,0 +1,22 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');

#__next {
min-height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
}

body {
font-family: 'Poppins', sans-serif;
}

@layer utilities {
.text-balance {
text-wrap: balance;
}
}
25 changes: 25 additions & 0 deletions code-samples/eventing/bookstore-sample-app/frontend/app/layout.tsx
@@ -0,0 +1,25 @@
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';

const inter = Inter({ subsets: ['latin'] });

export const metadata: Metadata = {
title: 'Knative Bookstore',
description: 'Bookstore Sample Application from Knative',
icons: {
icon: '/images/knative-logo.png',
},
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang='en'>
<body className={inter.className}>{children}</body>
</html>
);
}
11 changes: 11 additions & 0 deletions code-samples/eventing/bookstore-sample-app/frontend/app/page.tsx
@@ -0,0 +1,11 @@
'use client';
import Main from '../client/pages/Main';
import { ThemeProvider } from 'next-themes';

export default function Home() {
return (
<ThemeProvider attribute='class'>
<Main />
</ThemeProvider>
);
}
@@ -0,0 +1,43 @@
const BookDetail = ({ book }) => {
return (
<div className='flex flex-col md:flex-row md:items-start p-8 space-x-8 justify-center items-center font-sans'>
<div className='mb-4 md:mb-0 md:mr-8'>
<img src={book.img} alt='Book Cover' className='w-48 h-auto' />
</div>
<div>
<div className='space-y-4'>
<div className='grid grid-cols-[auto_1fr] gap-x-2 items-center'>
<span className='font-bold text-left'>Title:</span>
<span className='text-left'>{book.title}</span>
</div>
<div className='grid grid-cols-[auto_1fr] gap-x-2 items-center'>
<span className='font-bold text-left'>Author:</span>
<span className='text-left'>{book.author}</span>
</div>
<div className='grid grid-cols-[auto_1fr] gap-x-2 items-center'>
<span className='font-bold text-left'>ISBN:</span>
<span>{book.ISBN}</span>
</div>
<div className='grid grid-cols-[auto_1fr] gap-x-2 items-center'>
<span className='font-bold text-left'>Publisher:</span>
<span>{book.publisher}</span>
</div>
<div className='grid grid-cols-[auto_1fr] gap-x-2 items-center'>
<span className='font-bold text-left'>Published Date:</span>
<span>{book.publishedDate}</span>
</div>
<div className='grid grid-cols-[auto_1fr] gap-x-2 items-center'>
<span className='font-bold text-left'>Description:</span>
<span>{book.description}</span>
</div>
<div className='grid grid-cols-[auto_1fr] gap-x-2 items-center'>
<span className='font-bold text-left'>Price:</span>
<span>{book.price}</span>
</div>
</div>
</div>
</div>
);
};

export default BookDetail;
@@ -0,0 +1,38 @@
import Emoji from './Emoji';
const CommentDisplay = ({ comment }) => {
// Assume receiving a comment object
let emoji;
if (comment.emotion === 'Positive') {
emoji = '😃';
} else if (comment.emotion === 'Neutral') {
emoji = '😐';
} else {
emoji = '😡';
}
return (
<div className='flex my-4 p-4 justify-center align-middle items-center'>
<div className='comment-display w-full w-7/12 flex flex-row rounded-lg p-4 bg-gray-800 text-white dark:bg-white dark:text-black'>
<div className='flex items-center justify-center md:w-1/12'>
<img
src={comment.avatar}
alt='Avatar'
className='rounded-full w-8 h-8'
/>
</div>
<div className='md:w-1/12 flex items-center content-center text-gray-200 dark:text-black'>
{comment.time}
</div>
<div className='md:w-9/12 '>
<span className='h-full flex items-center content-center'>
{comment.text}
</span>
</div>
<div className='md:w-1/12 text-4xl flex items-center content-center'>
<Emoji symbol={emoji} label={comment.emotion} size='text-2xl' />
</div>
</div>
</div>
);
};

export default CommentDisplay;
@@ -0,0 +1,44 @@
'use-client';
import { useState } from 'react';
const CommentForm = () => {
const [hover, setHover] = useState(false);
const [comment, setComment] = useState('');

const handleInputChange = (event) => {
setComment(event.target.value);
};

const handleSubmit = (event) => {
event.preventDefault();
console.log('Submitted comment:', comment); // Use inspect to see
};
return (
<div className='flex my-4 p-4 justify-center'>
<form
className='w-full w-8/12 flex flex-col items-end '
onSubmit={handleSubmit}
>
<textarea
className='form-textarea w-full mb-2 p-2 border border-2 border-black rounded-lg p-4'
rows='3'
placeholder='Leave your comment here...'
value={comment}
onChange={handleInputChange}
></textarea>
<button
type='submit'
className={`font-bold py-2 px-9 rounded ${
hover ? '' : 'bg-blue-600'
}`}
style={{ backgroundColor: hover ? '#A0DDFF' : '#A5D8FF' }}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
Submit
</button>
</form>
</div>
);
};

export default CommentForm;
@@ -0,0 +1,12 @@
import CommentDisplay from './CommentDisplay';
const CommentList = () => {
const comment = {
avatar: '/images/avatar.jpg',
time: '10:05',
text: 'I used this provider to insert a different theme object depending on a person ',
emotion: 'Neutral',
};
return <CommentDisplay comment={comment} />;
};

export default CommentList;
@@ -0,0 +1,12 @@
import React from 'react';
const Emoji = (props) => (
<span
className='emoji'
role='img'
aria-label={props.label ? props.label : ''}
aria-hidden={props.label ? 'false' : 'true'}
>
{props.symbol}
</span>
);
export default Emoji;
@@ -0,0 +1,22 @@
import Image from 'next/image';
import Toggle from './Toggle';

const Header = () => {
return (
<div className='flex justify-between p-4'>
<header className='flex items-center p-4'>
<Image
src='/images/knative-logo.png'
alt='Knative BookStore Logo'
width={50}
height={50}
className='mr-2'
/>
<h1 className='text-3xl font-bold'>Knative BookStore</h1>
</header>
<Toggle />
</div>
);
};

export default Header;
@@ -0,0 +1,26 @@
'use client';
import React from 'react';
import { useTheme } from 'next-themes';
import { useEffect } from 'react';

const Toggle = () => {
const { systemTheme, theme, setTheme } = useTheme();
useEffect(() => {
const systemPreference = window.matchMedia('(prefers-color-scheme: dark)')
.matches
? 'dark'
: 'light';

setTheme(systemPreference);
}, []);
return (
<button
onClick={() => (theme == 'dark' ? setTheme('light') : setTheme('dark'))}
className='bg-gray-800 dark:bg-gray-50 hover:bg-gray-600 dark:hover:bg-gray-300 transition-all duration-100 text-white dark:text-gray-800 px-6 py-1 text-xl md:text-2xl rounded-md'
>
Mode
</button>
);
};

export default Toggle;

0 comments on commit 791daf5

Please sign in to comment.