(feat): feather icons ratings
This commit is contained in:
parent
7e4da741ec
commit
28d115af0e
@ -1,7 +1,8 @@
|
|||||||
|
// React
|
||||||
import React, { FC, useEffect } from 'react'
|
import React, { FC, useEffect } from 'react'
|
||||||
import { Route, Routes, useLocation } from 'react-router'
|
import { Route, Routes, useLocation } from 'react-router'
|
||||||
|
|
||||||
//Redux
|
// Redux
|
||||||
import { useSelector, useDispatch } from 'react-redux'
|
import { useSelector, useDispatch } from 'react-redux'
|
||||||
import { actionCreators as settingsActionCreators } from './store/reducers/Settings'
|
import { actionCreators as settingsActionCreators } from './store/reducers/Settings'
|
||||||
|
|
||||||
@ -33,8 +34,6 @@ const NestedRoutes = (routes: IRoute[], tag: string | undefined = undefined) =>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const App: FC = () => {
|
const App: FC = () => {
|
||||||
const { pathname } = useLocation()
|
const { pathname } = useLocation()
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
|||||||
@ -6,6 +6,7 @@ interface IFeatherIcon {
|
|||||||
size?: string | number,
|
size?: string | number,
|
||||||
className?: string,
|
className?: string,
|
||||||
fill?: string,
|
fill?: string,
|
||||||
|
stroke?: string,
|
||||||
otherProps?: any
|
otherProps?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ interface IFeatherIcon {
|
|||||||
*/
|
*/
|
||||||
const FeatherIcon : FC<IFeatherIcon> = (props : IFeatherIcon) => {
|
const FeatherIcon : FC<IFeatherIcon> = (props : IFeatherIcon) => {
|
||||||
|
|
||||||
const { icon, size = 24, className = '', fill = 'none', otherProps } = props
|
const { icon, size = 24, className = '', fill = 'none', stroke = 'black', otherProps } = props
|
||||||
|
|
||||||
if (!icon) {
|
if (!icon) {
|
||||||
return null
|
return null
|
||||||
@ -29,7 +30,7 @@ const FeatherIcon : FC<IFeatherIcon> = (props : IFeatherIcon) => {
|
|||||||
height={size}
|
height={size}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill={fill}
|
fill={fill}
|
||||||
stroke="currentColor"
|
stroke={stroke}
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
|
|||||||
57
clientapp/src/components/FeatherRating/index.tsx
Normal file
57
clientapp/src/components/FeatherRating/index.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import React, { FC } from 'react'
|
||||||
|
|
||||||
|
import { FeatherIcon } from '../FeatherIcons'
|
||||||
|
import { ICreateIconProps, ICreateIconResponse, IFeatherRating } from './interfaces'
|
||||||
|
|
||||||
|
const FeatherRating: FC<IFeatherRating> = ({
|
||||||
|
value,
|
||||||
|
icons = {
|
||||||
|
complete: { icon: "star", color: '#ffbf00' },
|
||||||
|
half: { icon: "star", color: '#ffdf80' },
|
||||||
|
empty: { icon: "star", color: '#ffffff' }
|
||||||
|
},
|
||||||
|
max = 5,
|
||||||
|
}) => {
|
||||||
|
const createIcon = ({ value, icons, max }: ICreateIconProps): ICreateIconResponse[] => {
|
||||||
|
let i = +value
|
||||||
|
|
||||||
|
const iconsToPrint: ICreateIconResponse[] = []
|
||||||
|
const { complete, half, empty } = icons
|
||||||
|
|
||||||
|
for (i; i >= 1; i -= 1) {
|
||||||
|
iconsToPrint.push({
|
||||||
|
icon: complete.icon,
|
||||||
|
fill: complete.color,
|
||||||
|
stroke: complete.color
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= 0.5 && i <= 1) {
|
||||||
|
iconsToPrint.push({
|
||||||
|
icon: half.icon,
|
||||||
|
fill: half.color,
|
||||||
|
stroke: half.color
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
while (iconsToPrint.length < max) {
|
||||||
|
iconsToPrint.push({
|
||||||
|
icon: empty.icon,
|
||||||
|
fill: empty.color,
|
||||||
|
stroke: empty.color
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return iconsToPrint
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
{createIcon({ value, icons, max }).map((icon, i) =>
|
||||||
|
<FeatherIcon key={i} {...icon} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
FeatherRating
|
||||||
|
}
|
||||||
36
clientapp/src/components/FeatherRating/interfaces.ts
Normal file
36
clientapp/src/components/FeatherRating/interfaces.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export interface IIcon {
|
||||||
|
icon: string,
|
||||||
|
color: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAllowIconsType {
|
||||||
|
complete: IIcon
|
||||||
|
half: IIcon
|
||||||
|
empty: IIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFeatherRating {
|
||||||
|
value: number
|
||||||
|
icons?: IAllowIconsType
|
||||||
|
max?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface ICreateIconProps {
|
||||||
|
value: number
|
||||||
|
icons: IAllowIconsType
|
||||||
|
max: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreateIconResponse {
|
||||||
|
icon: string
|
||||||
|
fill: string
|
||||||
|
stroke: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICustomStylesProps {
|
||||||
|
current: string
|
||||||
|
icons: IAllowIconsType
|
||||||
|
colors: string[]
|
||||||
|
type: string
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
const getKeyValue = <T extends object, U extends keyof T>(key: U) => (obj: T) => obj[key];
|
const getKeyValue = <T extends object, U extends keyof T>(key: U) => (obj: T) => obj[key]
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getKeyValue
|
getKeyValue
|
||||||
|
|||||||
46
clientapp/src/functions/jwtDecode/atob.ts
Normal file
46
clientapp/src/functions/jwtDecode/atob.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* The code was extracted from:
|
||||||
|
* https://github.com/davidchambers/Base64.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
|
||||||
|
|
||||||
|
class InvalidCharacterError extends Error {
|
||||||
|
message: string
|
||||||
|
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message)
|
||||||
|
|
||||||
|
// Set the prototype explicitly.
|
||||||
|
Object.setPrototypeOf(this, InvalidCharacterError.prototype)
|
||||||
|
|
||||||
|
this.message = message
|
||||||
|
this.name = 'InvalidCharacterError'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const polyfill = (input: any) => {
|
||||||
|
var str = String(input).replace(/=+$/, '')
|
||||||
|
if (str.length % 4 == 1) {
|
||||||
|
throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded.")
|
||||||
|
}
|
||||||
|
for (
|
||||||
|
// initialize result and counters
|
||||||
|
var bc = 0, bs = 0, buffer, idx = 0, output = '';
|
||||||
|
// get next character
|
||||||
|
buffer = str.charAt(idx++);
|
||||||
|
// character found in table? initialize bit storage and add its ascii value;
|
||||||
|
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
|
||||||
|
// and if not first of each 4 characters,
|
||||||
|
// convert the first 8 bits to one ascii character
|
||||||
|
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
|
||||||
|
) {
|
||||||
|
// try to find character in table (0-63, not found => -1)
|
||||||
|
buffer = chars.indexOf(buffer)
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
export default typeof window !== 'undefined' && window.atob && window.atob.bind(window) || polyfill
|
||||||
|
|
||||||
34
clientapp/src/functions/jwtDecode/base64_url_decode.ts
Normal file
34
clientapp/src/functions/jwtDecode/base64_url_decode.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import atob from './atob'
|
||||||
|
|
||||||
|
const b64DecodeUnicode = (str: string) => {
|
||||||
|
return decodeURIComponent(atob(str).replace(/(.)/g, function (m, p) {
|
||||||
|
var code = p.charCodeAt(0).toString(16).toUpperCase()
|
||||||
|
if (code.length < 2) {
|
||||||
|
code = '0' + code
|
||||||
|
}
|
||||||
|
return '%' + code
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (str: string) => {
|
||||||
|
var output = str.replace(/-/g, '+').replace(/_/g, '/')
|
||||||
|
switch (output.length % 4) {
|
||||||
|
case 0:
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
output += '=='
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
output += '='
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw 'Illegal base64url string!'
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return b64DecodeUnicode(output)
|
||||||
|
} catch (err) {
|
||||||
|
return atob(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
42
clientapp/src/functions/jwtDecode/index.ts
Normal file
42
clientapp/src/functions/jwtDecode/index.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
import base64_url_decode from './base64_url_decode'
|
||||||
|
|
||||||
|
class InvalidTokenError extends Error {
|
||||||
|
message: string
|
||||||
|
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message)
|
||||||
|
|
||||||
|
// Set the prototype explicitly.
|
||||||
|
Object.setPrototypeOf(this, InvalidTokenError.prototype)
|
||||||
|
|
||||||
|
this.message = message
|
||||||
|
this.name = 'InvalidTokenError'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IOptions {
|
||||||
|
header: boolean | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const JwtDecode = function (token: string, options: IOptions) {
|
||||||
|
if (typeof token !== 'string') {
|
||||||
|
throw new InvalidTokenError('Invalid token specified')
|
||||||
|
}
|
||||||
|
|
||||||
|
options = options || {}
|
||||||
|
var pos = options.header === true ? 0 : 1
|
||||||
|
try {
|
||||||
|
return JSON.parse(base64_url_decode(token.split('.')[pos]))
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw new InvalidTokenError('Invalid token specified: ' + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JwtDecode.InvalidTokenError = InvalidTokenError
|
||||||
|
|
||||||
|
export default JwtDecode
|
||||||
|
|
||||||
@ -20,4 +20,9 @@ export interface IMenuItem extends ISubMenuItem {
|
|||||||
items?: ISubMenuItem []
|
items?: ISubMenuItem []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IImage {
|
||||||
|
src: string,
|
||||||
|
alt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Card, CardBody, CardHeader, CardImg, Col, Container, Row } from 'reactstrap'
|
import { Card, CardBody, CardHeader, CardImg, Col, Container, Row } from 'reactstrap'
|
||||||
|
import { Categories, Empty, Search } from '../SideWidgets'
|
||||||
|
|
||||||
const BlogCatalog = () => {
|
const BlogCatalog = () => {
|
||||||
const items = [
|
const items = [
|
||||||
@ -46,7 +47,7 @@ const BlogCatalog = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
{items.map((item, index) => <Col key={index} lg={6}>
|
{items.map((item, index) => <Col key={index} className="lg-6">
|
||||||
<Card className="mb-4">
|
<Card className="mb-4">
|
||||||
|
|
||||||
<Link to={`${item.slug}`}>
|
<Link to={`${item.slug}`}>
|
||||||
@ -79,44 +80,10 @@ const BlogCatalog = () => {
|
|||||||
|
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
<Col className="lg-4">
|
<Col lg="4">
|
||||||
|
<Search />
|
||||||
<Card className="mb-4">
|
<Categories />
|
||||||
<CardHeader>Search</CardHeader>
|
<Empty/>
|
||||||
<CardBody>
|
|
||||||
<div className="input-group">
|
|
||||||
<input className="form-control" type="text" placeholder="Enter search term..." aria-label="Enter search term..." aria-describedby="button-search" />
|
|
||||||
<button className="btn btn-primary" id="button-search" type="button">Go!</button>
|
|
||||||
</div>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="mb-4">
|
|
||||||
<CardHeader>Categories</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
<Row>
|
|
||||||
<Col className="sm-6">
|
|
||||||
<ul className="list-unstyled mb-0">
|
|
||||||
<li><a href="#!">Web Design</a></li>
|
|
||||||
<li><a href="#!">HTML</a></li>
|
|
||||||
<li><a href="#!">Freebies</a></li>
|
|
||||||
</ul>
|
|
||||||
</Col>
|
|
||||||
<Col sm={6}>
|
|
||||||
<ul className="list-unstyled mb-0">
|
|
||||||
<li><a href="#!">JavaScript</a></li>
|
|
||||||
<li><a href="#!">CSS</a></li>
|
|
||||||
<li><a href="#!">Tutorials</a></li>
|
|
||||||
</ul>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="mb-4">
|
|
||||||
<CardHeader>Side Widget</CardHeader>
|
|
||||||
<CardBody>You can put anything you want inside of these side widgets. They are easy to use, and feature the Bootstrap 5 card component!</CardBody>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Card, CardBody, CardHeader, Col, Container, Row } from 'reactstrap'
|
import { Card, CardBody, CardHeader, Col, Container, Row } from 'reactstrap'
|
||||||
|
import { Categories, Empty, Search } from '../SideWidgets'
|
||||||
|
|
||||||
const BlogItem = () => {
|
const BlogItem = () => {
|
||||||
|
|
||||||
@ -8,9 +9,9 @@ const BlogItem = () => {
|
|||||||
comments: []
|
comments: []
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Container fluid className="mt-5">
|
return <Container fluid mt="5">
|
||||||
<Row>
|
<Row>
|
||||||
<Col className="lg-8">
|
<Col lg="8">
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<header className="mb-4">
|
<header className="mb-4">
|
||||||
@ -41,9 +42,8 @@ const BlogItem = () => {
|
|||||||
<section className="mb-5">
|
<section className="mb-5">
|
||||||
<div className="card bg-light">
|
<div className="card bg-light">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<form className="mb-4">
|
<form className="mb-4">
|
||||||
|
<textarea className="form-control" rows={3} placeholder="Join the discussion and leave a comment!"></textarea>
|
||||||
{/*<textarea class="form-control" rows="3" placeholder="Join the discussion and leave a comment!"></textarea>*/}
|
|
||||||
</form>
|
</form>
|
||||||
<div className="d-flex mb-4">
|
<div className="d-flex mb-4">
|
||||||
|
|
||||||
@ -84,45 +84,10 @@ const BlogItem = () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Col className="lg-4">
|
<Col lg="4">
|
||||||
<Card className="mb-4">
|
<Search />
|
||||||
<CardHeader>Search</CardHeader>
|
<Categories />
|
||||||
<CardBody>
|
<Empty/>
|
||||||
<div className="input-group">
|
|
||||||
<input className="form-control" type="text" placeholder="Enter search term..." aria-label="Enter search term..." aria-describedby="button-search" />
|
|
||||||
<button className="btn btn-primary" id="button-search" type="button">Go!</button>
|
|
||||||
</div>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="mb-4">
|
|
||||||
<CardHeader>Categories</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
<Row>
|
|
||||||
<Col className="sm-6">
|
|
||||||
<ul className="list-unstyled mb-0">
|
|
||||||
<li><a href="#!">Web Design</a></li>
|
|
||||||
<li><a href="#!">HTML</a></li>
|
|
||||||
<li><a href="#!">Freebies</a></li>
|
|
||||||
</ul>
|
|
||||||
</Col>
|
|
||||||
<Col className="sm-6">
|
|
||||||
<ul className="list-unstyled mb-0">
|
|
||||||
<li><a href="#!">JavaScript</a></li>
|
|
||||||
<li><a href="#!">CSS</a></li>
|
|
||||||
<li><a href="#!">Tutorials</a></li>
|
|
||||||
</ul>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="mb-4">
|
|
||||||
<CardHeader>Side Widget</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
You can put anything you want inside of these side widgets. They are easy to use, and feature the Bootstrap 5 card component!
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@ -1,5 +1,53 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { Card, CardBody, CardHeader, Col, Row } from 'reactstrap'
|
||||||
|
|
||||||
const SideWidgets = () => {
|
const Search = () => {
|
||||||
return null
|
return <Card className="mb-4">
|
||||||
|
<CardHeader>Search</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<div className="input-group">
|
||||||
|
<input className="form-control" type="text" placeholder="Enter search term..." aria-label="Enter search term..." aria-describedby="button-search" />
|
||||||
|
<button className="btn btn-primary" id="button-search" type="button">Go!</button>
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Categories = () => {
|
||||||
|
return <Card className="mb-4">
|
||||||
|
<CardHeader>Categories</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<Row>
|
||||||
|
<Col sm="6">
|
||||||
|
<ul className="list-unstyled mb-0">
|
||||||
|
<li><a href="#!">Web Design</a></li>
|
||||||
|
<li><a href="#!">HTML</a></li>
|
||||||
|
<li><a href="#!">Freebies</a></li>
|
||||||
|
</ul>
|
||||||
|
</Col>
|
||||||
|
<Col sm="6">
|
||||||
|
<ul className="list-unstyled mb-0">
|
||||||
|
<li><a href="#!">JavaScript</a></li>
|
||||||
|
<li><a href="#!">CSS</a></li>
|
||||||
|
<li><a href="#!">Tutorials</a></li>
|
||||||
|
</ul>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Empty = () => {
|
||||||
|
return <Card className="mb-4">
|
||||||
|
<CardHeader>Side Widget</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
You can put anything you want inside of these side widgets. They are easy to use, and feature the Bootstrap 5 card component!
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Search,
|
||||||
|
Categories,
|
||||||
|
Empty
|
||||||
}
|
}
|
||||||
@ -2,32 +2,10 @@ import React, { FC } from 'react'
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap'
|
import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap'
|
||||||
import { FeatherIcon } from '../../components/FeatherIcons'
|
import { FeatherIcon } from '../../components/FeatherIcons'
|
||||||
|
import { IImage } from '../../interfaces'
|
||||||
|
|
||||||
import style from './scss/style.module.scss'
|
import style from './scss/style.module.scss'
|
||||||
|
|
||||||
|
|
||||||
interface IImage {
|
|
||||||
src: string,
|
|
||||||
alt: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IBlogAuthor {
|
|
||||||
name: string,
|
|
||||||
image: IImage
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IBlogItem {
|
|
||||||
image: IImage,
|
|
||||||
badge: string,
|
|
||||||
title: string,
|
|
||||||
text: string,
|
|
||||||
author: IBlogAuthor,
|
|
||||||
date: string,
|
|
||||||
readTime: string
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface ITitleSection {
|
interface ITitleSection {
|
||||||
title: string,
|
title: string,
|
||||||
text: string
|
text: string
|
||||||
@ -120,6 +98,21 @@ const TestimonialsSection: FC<ITestimonialsSection> = (props) => {
|
|||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IBlogAuthor {
|
||||||
|
name: string,
|
||||||
|
image: IImage
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IBlogItem {
|
||||||
|
image: IImage,
|
||||||
|
badge: string,
|
||||||
|
title: string,
|
||||||
|
text: string,
|
||||||
|
author: IBlogAuthor,
|
||||||
|
date: string,
|
||||||
|
readTime: string
|
||||||
|
}
|
||||||
|
|
||||||
interface IFromOurBlogSection {
|
interface IFromOurBlogSection {
|
||||||
title: string,
|
title: string,
|
||||||
text: string,
|
text: string,
|
||||||
@ -205,7 +198,7 @@ const Home = () => {
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href='https://get.asp.net/'>ASP.NET Core</a> and <a href='https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx'>C#</a> for cross-platform server-side code</li>
|
<li><a href='https://get.asp.net/'>ASP.NET Core</a> and <a href='https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx'>C#</a> for cross-platform server-side code</li>
|
||||||
<li><a href='https://facebook.github.io/react/'>React</a> and <a href='https://redux.js.org/'>Redux</a> for client-side code</li>
|
<li><a href='https://facebook.github.io/react/'>React</a> and <a href='https://redux.js.org/'>Redux</a> for client-side code</li>
|
||||||
<li><a href='https://getbootstrap.com/'>Bootstrap</a> + <a href='https://reactstrap.github.io/?path=/story/home-installation--page'>Reactstrap</a> + <a href="https://feathericons.com/">Feather icons</a> for layout and styling</li>
|
<li><a href='https://getbootstrap.com/'>Bootstrap</a>, <a href='https://reactstrap.github.io/?path=/story/home-installation--page'>Reactstrap</a> and <a href="https://feathericons.com/">Feather icons</a> for layout and styling</li>
|
||||||
</ul>
|
</ul>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,32 +2,55 @@ import * as React from 'react'
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap'
|
import { Card, CardBody, CardFooter, CardImg, Col, Container, Row } from 'reactstrap'
|
||||||
|
|
||||||
|
import { FeatherRating } from '../../../components/FeatherRating'
|
||||||
|
|
||||||
const ShopCatalog = () => {
|
const ShopCatalog = () => {
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
id: "1"
|
id: "1",
|
||||||
|
rating: 5,
|
||||||
|
price: "$20.00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "2"
|
id: "2",
|
||||||
|
rating: 3.5,
|
||||||
|
price: "$20.00",
|
||||||
|
newPrice: "$10.00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "3"
|
id: "3",
|
||||||
|
rating: 2,
|
||||||
|
price: "$20.00",
|
||||||
|
newPrice: "$10.00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "4"
|
id: "4",
|
||||||
|
rating: 4,
|
||||||
|
price: "$20.00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "5"
|
id: "5",
|
||||||
|
rating: 4.5,
|
||||||
|
price: "$20.00",
|
||||||
|
newPrice: "$10.00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "6"
|
id: "6",
|
||||||
|
rating: 5,
|
||||||
|
price: "$20.00",
|
||||||
|
newPrice: "$10.00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "7"
|
id: "7",
|
||||||
|
rating: 2,
|
||||||
|
price: "$20.00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "8"
|
id: "8",
|
||||||
|
rating: 3,
|
||||||
|
price: "$20.00",
|
||||||
|
newPrice: "$10.00"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -55,16 +78,14 @@ const ShopCatalog = () => {
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h5 className="fw-bolder">Fancy Product</h5>
|
<h5 className="fw-bolder">Fancy Product</h5>
|
||||||
|
|
||||||
<div className="d-flex justify-content-center small text-warning mb-2">
|
<FeatherRating {...{
|
||||||
<div className="bi-star-fill"></div>
|
value: item.rating
|
||||||
<div className="bi-star-fill"></div>
|
}} />
|
||||||
<div className="bi-star-fill"></div>
|
|
||||||
<div className="bi-star-fill"></div>
|
{item.newPrice
|
||||||
<div className="bi-star-fill"></div>
|
? <><span className="text-muted text-decoration-line-through">{item.price}</span> {item.newPrice}</>
|
||||||
</div>
|
: item.price}
|
||||||
|
|
||||||
<span className="text-muted text-decoration-line-through">$20.00</span>
|
|
||||||
$10.00
|
|
||||||
</div>
|
</div>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
<CardFooter className="p-4 pt-0 border-top-0 bg-transparent">
|
<CardFooter className="p-4 pt-0 border-top-0 bg-transparent">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user