├── .gitignore
├── src
├── css
│ ├── _animations-finished.styl
│ ├── _colours.styl
│ ├── fonts
│ │ ├── haymaker-webfont.eot
│ │ ├── haymaker-webfont.ttf
│ │ ├── haymaker-webfont.woff
│ │ ├── blanch_caps_inline-webfont.eot
│ │ ├── blanch_caps_inline-webfont.ttf
│ │ ├── blanch_caps_inline-webfont.woff
│ │ ├── haymaker-webfont.svg
│ │ └── blanch_caps_inline-webfont.svg
│ ├── _typography.styl
│ ├── _fonts.styl
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── images
│ │ └── anchor.svg
│ ├── style.styl
│ └── style.css
├── components
│ ├── .gitkeep
│ ├── NotFound.js
│ ├── Router.js
│ ├── Login.js
│ ├── Header.js
│ ├── StorePicker.js
│ ├── Fish.js
│ ├── AddFishForm.js
│ ├── EditFishForm.js
│ ├── Order.js
│ ├── Inventory.js
│ └── App.js
├── index.js
├── base.js
├── helpers.js
└── sample-fishes.js
├── .netlify
└── state.json
├── public
├── favicon.ico
├── images
│ ├── crab.jpg
│ ├── hali.jpg
│ ├── mahi.jpg
│ ├── lobster.jpg
│ ├── mussels.jpg
│ ├── oysters.jpg
│ ├── prawns.jpg
│ ├── salmon.jpg
│ └── scallops.jpg
└── index.html
├── security-rules.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/src/css/_animations-finished.styl:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.netlify/state.json:
--------------------------------------------------------------------------------
1 | {
2 | "siteId": "24bacee9-4806-469b-896a-3e0c2f9b1a0c"
3 | }
--------------------------------------------------------------------------------
/src/css/_colours.styl:
--------------------------------------------------------------------------------
1 | orange = #F5A623
2 | red = #d12028
3 | green = #2DC22D
4 |
--------------------------------------------------------------------------------
/src/components/.gitkeep:
--------------------------------------------------------------------------------
1 | // this is just an empty file so the empty folder will stay in git!
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/crab.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/crab.jpg
--------------------------------------------------------------------------------
/public/images/hali.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/hali.jpg
--------------------------------------------------------------------------------
/public/images/mahi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/mahi.jpg
--------------------------------------------------------------------------------
/public/images/lobster.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/lobster.jpg
--------------------------------------------------------------------------------
/public/images/mussels.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/mussels.jpg
--------------------------------------------------------------------------------
/public/images/oysters.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/oysters.jpg
--------------------------------------------------------------------------------
/public/images/prawns.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/prawns.jpg
--------------------------------------------------------------------------------
/public/images/salmon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/salmon.jpg
--------------------------------------------------------------------------------
/public/images/scallops.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/public/images/scallops.jpg
--------------------------------------------------------------------------------
/src/css/fonts/haymaker-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/src/css/fonts/haymaker-webfont.eot
--------------------------------------------------------------------------------
/src/css/fonts/haymaker-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/src/css/fonts/haymaker-webfont.ttf
--------------------------------------------------------------------------------
/src/css/fonts/haymaker-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/src/css/fonts/haymaker-webfont.woff
--------------------------------------------------------------------------------
/src/css/fonts/blanch_caps_inline-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/src/css/fonts/blanch_caps_inline-webfont.eot
--------------------------------------------------------------------------------
/src/css/fonts/blanch_caps_inline-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/src/css/fonts/blanch_caps_inline-webfont.ttf
--------------------------------------------------------------------------------
/src/css/fonts/blanch_caps_inline-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikkifurls/tutorial-react-wes-bos/master/src/css/fonts/blanch_caps_inline-webfont.woff
--------------------------------------------------------------------------------
/src/components/NotFound.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const NotFound = () => (
4 |
5 |
Not Found
6 |
7 | );
8 |
9 | export default NotFound;
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import Router from './components/Router';
4 | import "./css/style.css";
5 |
6 | render(, document.querySelector('#main'));
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Catch of the Day
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/security-rules.json:
--------------------------------------------------------------------------------
1 | // These are your firebase security rules - put them in the "Security & Rules" tab of your database
2 | {
3 | "rules": {
4 | // won't let people delete an existing room
5 | ".write": "!data.exists()",
6 | ".read": true,
7 | "$room": {
8 | // only the store owner can edit the data
9 | ".write":
10 | "auth != null && (!data.exists() || data.child('owner').val() === auth.uid)",
11 | ".read": true
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Router.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter, Route, Switch } from 'react-router-dom';
3 | import StorePicker from './StorePicker';
4 | import App from './App';
5 | import NotFound from './NotFound';
6 |
7 | const Router = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | )
16 |
17 | export default Router;
--------------------------------------------------------------------------------
/src/components/Login.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const Login = props => (
5 |
15 | );
16 |
17 | Login.propTypes = {
18 | authenticate: PropTypes.func.isRequired
19 | }
20 |
21 | export default Login;
--------------------------------------------------------------------------------
/src/base.js:
--------------------------------------------------------------------------------
1 | import Rebase from 're-base';
2 | import firebase from 'firebase';
3 |
4 | const firebaseApp = firebase.initializeApp({
5 | apiKey: "AIzaSyCj1JLw3V4IFl0WumX9HKT1yG6iBh_mCB4",
6 | authDomain: "nicole-catch-of-the-day.firebaseapp.com",
7 | databaseURL: "https://nicole-catch-of-the-day-default-rtdb.firebaseio.com",
8 | // measurementId: "G-VYXTX1X678"
9 | });
10 |
11 | // Rebase bindings
12 | const base = Rebase.createClass(firebaseApp.database());
13 |
14 | // Named export
15 | export { firebaseApp };
16 |
17 | // Default export
18 | export default base;
--------------------------------------------------------------------------------
/src/css/_typography.styl:
--------------------------------------------------------------------------------
1 | html
2 | font-size 62.5%
3 |
4 | body
5 | background #D4D4D4
6 | -webkit-font-smoothing antialiased
7 | -moz-osx-font-smoothing grayscale
8 | font-family 'Open Sans', sans-serif
9 | font-size 2rem
10 |
11 | h1
12 | font-family 'blanchcaps_inline', sans-serif;
13 | text-align center
14 | font-weight normal
15 | margin 0
16 |
17 |
18 |
19 | h2,h3,h4,h5,h6
20 | font-weight normal
21 | font-family 'haymakerregular', sans-serif
22 |
23 | h2
24 | text-align center
25 | margin-top 0
26 | margin-bottom 2rem
27 |
28 | h3
29 | font-size 3rem
30 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | // Stateless functional component
5 | const Header = props => (
6 |
7 |
8 | Catch
9 |
10 | of
11 | the
12 |
13 | Day
14 |
15 |
16 | {props.tagline}
17 |
18 |
19 | );
20 |
21 | Header.propTypes = {
22 | tagline: PropTypes.string.isRequired
23 | };
24 |
25 | export default Header;
--------------------------------------------------------------------------------
/src/css/_fonts.styl:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "haymakerregular";
3 | src: url("./fonts/haymaker-webfont.eot");
4 | src: url("./fonts/haymaker-webfont.eot?#iefix") format("embedded-opentype"), url("./fonts/haymaker-webfont.woff") format("woff"), url("./fonts/haymaker-webfont.ttf") format("truetype"), url("./fonts/haymaker-webfont.svg#haymakerregular") format("svg");
5 | font-weight: normal;
6 | font-style: normal;
7 | }
8 |
9 | @font-face {
10 | font-family: 'blanchcaps_inline';
11 | src: url('./fonts/blanch_caps_inline-webfont.eot');
12 | src: url('./fonts/blanch_caps_inline-webfont.eot?#iefix') format('embedded-opentype'),
13 | url('./fonts/blanch_caps_inline-webfont.woff') format('woff'),
14 | url('./fonts/blanch_caps_inline-webfont.ttf') format('truetype'),
15 | url('./fonts/blanch_caps_inline-webfont.svg#blanchcaps_inline') format('svg');
16 | font-weight: normal;
17 | font-style: normal;
18 | }
19 |
--------------------------------------------------------------------------------
/src/css/_animations.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Possible Animations:
3 | enter
4 | exit
5 | appear Must set transitionAppear={true} on animation component
6 | */
7 |
8 | .order-enter
9 | transform: translateX(-120%)
10 | transition 0.5s
11 | max-height: 0
12 | padding 0 !important
13 | &.order-enter-active
14 | max-height 60px
15 | transform: translateX(0)
16 | padding 2rem 0 !important
17 |
18 | .order-exit
19 | transition 0.5s
20 | transform: translateX(0)
21 | &.order-exit-active
22 | transform: translateX(120%)
23 | padding: 0
24 |
25 |
26 | .count-enter
27 | background: red
28 | transition .5s
29 | transform translateY(100%)
30 | &.count-enter-active
31 | background: yellow
32 | transform translateY(0)
33 |
34 | .count-exit
35 | background: black
36 | transform translateY(0)
37 | transition .5s
38 | position absolute
39 | left 0
40 | bottom 0
41 | &.count-exit-active
42 | background green
43 | transform translateY(-100%) scale(3)
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cotd",
3 | "version": "0.0.3",
4 | "private": true,
5 | "devDependencies": {
6 | "concurrently": "4.1.0",
7 | "react-scripts": "3.4.1"
8 | },
9 | "dependencies": {
10 | "autoprefixer-stylus": "0.14.0",
11 | "firebase": "^7.6.0",
12 | "prop-types": "^15.6.0",
13 | "re-base": "4.0.0",
14 | "react": "^16.6.3",
15 | "react-dom": "^16.6.3",
16 | "react-router-dom": "^4.2.2",
17 | "react-transition-group": "^2.2.1",
18 | "serve": "^10.1.2",
19 | "stylus": "0.54.5"
20 | },
21 | "scripts": {
22 | "dev": "react-scripts start",
23 | "start": "serve --single ./build",
24 | "watch": "concurrently --names \"webpack, stylus\" --prefix name \"npm run start\" \"npm run styles:watch\"",
25 | "build": "react-scripts build",
26 | "eject": "react-scripts eject",
27 | "styles": "stylus -u autoprefixer-stylus ./src/css/style.styl -o ./src/css/style.css",
28 | "now-build": "npm run build && mv build dist",
29 | "styles:watch": "npm run styles -- -w"
30 | },
31 | "browserslist": [
32 | ">0.2%",
33 | "not ie <= 11",
34 | "not op_mini all"
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/StorePicker.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { getFunName } from "../helpers";
4 |
5 | class StorePicker extends React.Component {
6 |
7 | // Set component properties
8 | myInput = React.createRef();
9 |
10 | static propTypes = {
11 | history: PropTypes.object
12 | }
13 |
14 | goToStore = event => {
15 | // 1. Stop the form from submitting
16 | event.preventDefault();
17 |
18 | // 2. Get the text from the input
19 | const storeName = this.myInput.current.value;
20 |
21 | // 3. Change the page to /store/whatever-they-entered
22 | this.props.history.push(`/store/${storeName}`);
23 | }
24 |
25 | render() {
26 | return (
27 |
38 | );
39 | }
40 | }
41 |
42 | export default StorePicker;
--------------------------------------------------------------------------------
/src/components/Fish.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { formatPrice } from '../helpers';
4 |
5 | class Fish extends React.Component {
6 |
7 | static propTypes = {
8 | index: PropTypes.string,
9 | details: PropTypes.shape({
10 | image: PropTypes.string,
11 | name: PropTypes.string,
12 | desc: PropTypes.string,
13 | status: PropTypes.string,
14 | price: PropTypes.number
15 | }),
16 | addToOrder: PropTypes.func
17 | }
18 |
19 | render() {
20 | const { image, name, price, desc, status } = this.props.details;
21 | const isAvailable = status === 'available';
22 |
23 | return (
24 |
25 |
26 |
27 | {name}
28 | {formatPrice(price)}
29 |
30 | {desc}
31 |
34 |
35 | );
36 | }
37 | }
38 |
39 | export default Fish;
--------------------------------------------------------------------------------
/src/components/AddFishForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | class AddFishForm extends React.Component {
5 | nameRef = React.createRef();
6 | priceRef = React.createRef();
7 | statusRef = React.createRef();
8 | descRef = React.createRef();
9 | imageRef = React.createRef();
10 |
11 | static propTypes = {
12 | addFish: PropTypes.func
13 | }
14 |
15 | createFish = event => {
16 | // 1. Stop the form from submitting
17 | event.preventDefault();
18 |
19 | // 2. Create fish
20 | const fish = {
21 | name: this.nameRef.current.value,
22 | price: parseFloat(this.priceRef.current.value),
23 | status: this.statusRef.current.value,
24 | desc: this.descRef.current.value,
25 | image: this.imageRef.current.value,
26 | }
27 |
28 | this.props.addFish(fish);
29 |
30 | // 3. Refresh form
31 | event.currentTarget.reset();
32 | }
33 |
34 | render() {
35 | return (
36 |
47 | );
48 | }
49 | }
50 |
51 | export default AddFishForm;
--------------------------------------------------------------------------------
/src/css/_normalize.styl:
--------------------------------------------------------------------------------
1 | // CSS Normalize
2 |
3 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button], input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
4 |
5 | // clearfix
6 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
7 |
8 | // Sane border box
9 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
10 |
--------------------------------------------------------------------------------
/src/components/EditFishForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | class EditFishForm extends React.Component {
5 |
6 | static propTypes = {
7 | fish: PropTypes.shape({
8 | image: PropTypes.string,
9 | name: PropTypes.string,
10 | desc: PropTypes.string,
11 | status: PropTypes.string,
12 | price: PropTypes.number
13 | }),
14 | index: PropTypes.string,
15 | updateFish: PropTypes.func
16 | }
17 |
18 | handleChange = event => {
19 | // Update fish
20 | // 1. Copy current fish
21 | const updatedFish = {
22 | ...this.props.fish,
23 | [event.currentTarget.name]: event.currentTarget.value
24 | }
25 | this.props.updateFish(this.props.index, updatedFish);
26 | };
27 |
28 | render() {
29 | return (
30 |
31 |
37 |
43 |
50 |
54 |
59 |
60 |
61 | );
62 | }
63 | }
64 |
65 | export default EditFishForm;
--------------------------------------------------------------------------------
/src/helpers.js:
--------------------------------------------------------------------------------
1 | export function formatPrice(cents) {
2 | return (cents / 100).toLocaleString("en-US", {
3 | style: "currency",
4 | currency: "USD"
5 | });
6 | }
7 |
8 | export function rando(arr) {
9 | return arr[Math.floor(Math.random() * arr.length)];
10 | }
11 |
12 | export function slugify(text) {
13 | return text
14 | .toString()
15 | .toLowerCase()
16 | .replace(/\s+/g, "-")
17 | .replace(/[^\w-]+/g, "")
18 | .replace(/--+/g, "-")
19 | .replace(/^-+/, "")
20 | .replace(/-+$/, "");
21 | }
22 |
23 | export function getFunName() {
24 | const adjectives = [
25 | "adorable",
26 | "beautiful",
27 | "clean",
28 | "drab",
29 | "elegant",
30 | "fancy",
31 | "glamorous",
32 | "handsome",
33 | "long",
34 | "magnificent",
35 | "old-fashioned",
36 | "plain",
37 | "quaint",
38 | "sparkling",
39 | "ugliest",
40 | "unsightly",
41 | "angry",
42 | "bewildered",
43 | "clumsy",
44 | "defeated",
45 | "embarrassed",
46 | "fierce",
47 | "grumpy",
48 | "helpless",
49 | "itchy",
50 | "jealous",
51 | "lazy",
52 | "mysterious",
53 | "nervous",
54 | "obnoxious",
55 | "panicky",
56 | "repulsive",
57 | "scary",
58 | "thoughtless",
59 | "uptight",
60 | "worried"
61 | ];
62 |
63 | const nouns = [
64 | "women",
65 | "men",
66 | "children",
67 | "teeth",
68 | "feet",
69 | "people",
70 | "leaves",
71 | "mice",
72 | "geese",
73 | "halves",
74 | "knives",
75 | "wives",
76 | "lives",
77 | "elves",
78 | "loaves",
79 | "potatoes",
80 | "tomatoes",
81 | "cacti",
82 | "foci",
83 | "fungi",
84 | "nuclei",
85 | "syllabuses",
86 | "analyses",
87 | "diagnoses",
88 | "oases",
89 | "theses",
90 | "crises",
91 | "phenomena",
92 | "criteria",
93 | "data"
94 | ];
95 |
96 | return `${rando(adjectives)}-${rando(adjectives)}-${rando(nouns)}`;
97 | }
98 |
--------------------------------------------------------------------------------
/src/sample-fishes.js:
--------------------------------------------------------------------------------
1 | // This is just some sample data so you don't have to think of your own!
2 | const fishes = {
3 | fish1: {
4 | name: "Pacific Halibut",
5 | image: "/images/hali.jpg",
6 | desc:
7 | "Everyone’s favorite white fish. We will cut it to the size you need and ship it.",
8 | price: 1724,
9 | status: "available"
10 | },
11 |
12 | fish2: {
13 | name: "Lobster",
14 | image: "/images/lobster.jpg",
15 | desc:
16 | "These tender, mouth-watering beauties are a fantastic hit at any dinner party.",
17 | price: 3200,
18 | status: "available"
19 | },
20 |
21 | fish3: {
22 | name: "Sea Scallops",
23 | image: "/images/scallops.jpg",
24 | desc:
25 | "Big, sweet and tender. True dry-pack scallops from the icey waters of Alaska. About 8-10 per pound",
26 | price: 1684,
27 | status: "unavailable"
28 | },
29 |
30 | fish4: {
31 | name: "Mahi Mahi",
32 | image: "/images/mahi.jpg",
33 | desc:
34 | "Lean flesh with a mild, sweet flavor profile, moderately firm texture and large, moist flakes. ",
35 | price: 1129,
36 | status: "available"
37 | },
38 |
39 | fish5: {
40 | name: "King Crab",
41 | image: "/images/crab.jpg",
42 | desc:
43 | "Crack these open and enjoy them plain or with one of our cocktail sauces",
44 | price: 4234,
45 | status: "available"
46 | },
47 |
48 | fish6: {
49 | name: "Atlantic Salmon",
50 | image: "/images/salmon.jpg",
51 | desc:
52 | "This flaky, oily salmon is truly the king of the sea. Bake it, grill it, broil it...as good as it gets!",
53 | price: 1453,
54 | status: "available"
55 | },
56 |
57 | fish7: {
58 | name: "Oysters",
59 | image: "/images/oysters.jpg",
60 | desc: "A soft plump oyster with a sweet salty flavor and a clean finish.",
61 | price: 2543,
62 | status: "available"
63 | },
64 |
65 | fish8: {
66 | name: "Mussels",
67 | image: "/images/mussels.jpg",
68 | desc:
69 | "The best mussels from the Pacific Northwest with a full-flavored and complex taste.",
70 | price: 425,
71 | status: "available"
72 | },
73 |
74 | fish9: {
75 | name: "Jumbo Prawns",
76 | image: "/images/prawns.jpg",
77 | desc:
78 | "With 21-25 two bite prawns in each pound, these sweet morsels are perfect for shish-kabobs.",
79 | price: 2250,
80 | status: "available"
81 | }
82 | };
83 |
84 | export default fishes;
85 |
--------------------------------------------------------------------------------
/src/components/Order.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { formatPrice } from '../helpers';
4 | import { TransitionGroup, CSSTransition } from 'react-transition-group';
5 |
6 | class Order extends React.Component {
7 |
8 | static propTypes = {
9 | fishes: PropTypes.object,
10 | order: PropTypes.object,
11 | removeFromOrder: PropTypes.func
12 | }
13 |
14 | renderOrder = key => {
15 | const fish = this.props.fishes[key];
16 | const count = this.props.order[key];
17 | const isAvailable = fish && fish.status === 'available';
18 | const transitionOptions = {
19 | classNames: "order",
20 | key,
21 | timeout: { enter: 500, exit: 500 }
22 | };
23 |
24 | // Make sure fish is loaded
25 | if (!fish) return null;
26 |
27 | if (!isAvailable) {
28 | return (
29 |
30 |
31 | Sorry {fish ? fish.name : 'fish'} is no longer available
32 |
33 |
34 |
35 | )
36 | }
37 |
38 | return (
39 |
40 |
41 |
42 |
43 |
44 | {count}
45 |
46 |
47 | lbs {fish.name} {formatPrice(count * fish.price)}
48 |
49 |
50 |
51 |
52 | )
53 | }
54 |
55 | render() {
56 | const orderIds = Object.keys(this.props.order);
57 | const total = orderIds.reduce((prevTotal, key) => {
58 | const fish = this.props.fishes[key];
59 | const count = this.props.order[key];
60 | const isAvailable = fish && fish.status === 'available';
61 | if (isAvailable) {
62 | return prevTotal + (count * fish.price);
63 | }
64 | return prevTotal;
65 | }, 0);
66 |
67 | return (
68 |
69 |
Order
70 |
71 | {orderIds.map(this.renderOrder)}
72 |
73 |
{formatPrice(total)}
74 |
75 | );
76 | }
77 | }
78 |
79 | export default Order;
--------------------------------------------------------------------------------
/src/components/Inventory.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import AddFishForm from './AddFishForm';
4 | import EditFishForm from './EditFishForm';
5 | import Login from './Login';
6 | import base, { firebaseApp } from '../base';
7 | import firebase from 'firebase';
8 |
9 | class Inventory extends React.Component {
10 |
11 | static propTypes = {
12 | storeId: PropTypes.string,
13 | fishes: PropTypes.object,
14 | updateFish: PropTypes.func,
15 | deleteFish: PropTypes.func,
16 | loadSampleFishes: PropTypes.func,
17 | };
18 |
19 | state = {
20 | uid: null,
21 | onwer: null
22 | };
23 |
24 | componentDidMount() {
25 | firebase.auth().onAuthStateChanged(user => {
26 | if (user) {
27 | this.authHandler({ user });
28 | }
29 | })
30 | }
31 |
32 | authHandler = async authData => {
33 | // 1. Look up current store in Firebase database
34 | const store = await base.fetch(this.props.storeId, { context: this });
35 |
36 | // 2. Claim it if there is no owner
37 | if (!store.owner) {
38 | // Save it as our own
39 | await base.post(`${this.props.storeId}/owner`, {
40 | data: authData.user.uid
41 | })
42 | }
43 |
44 | // 3. Set state of inventory component to reflect current user
45 | this.setState({
46 | uid: authData.user.uid,
47 | owner: store.owner || authData.user.uid,
48 | });
49 | };
50 |
51 | authenticate = provider => {
52 | // Auth provider for FB
53 | const authProvider = new firebase.auth[`${provider}AuthProvider`]();
54 | firebaseApp.auth().signInWithPopup(authProvider).then(this.authHandler);
55 | }
56 |
57 | logout = async () => {
58 | await firebase.auth().signOut();
59 | this.setState({ uid: null });
60 | }
61 |
62 | render() {
63 | const logout = ;
64 |
65 | // 1. Check if logged in
66 | if (!this.state.uid) {
67 | return ;
68 | }
69 |
70 | // 2. Check if not owner
71 | if (this.state.uid !== this.state.owner) {
72 | return (
73 |
74 |
Sorry, you are not the owner!
75 | {logout}
76 |
77 | );
78 | }
79 |
80 | // 3. Render inventory
81 | return (
82 |
83 |
Inventory
84 | {logout}
85 | {Object.keys(this.props.fishes).map(key => (
86 |
93 | ))}
94 |
97 |
98 |
99 | );
100 | }
101 | }
102 |
103 | export default Inventory;
--------------------------------------------------------------------------------
/src/css/images/anchor.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Header from './Header';
4 | import Order from './Order';
5 | import Inventory from './Inventory';
6 | import sampleFishes from "../sample-fishes";
7 | import Fish from './Fish';
8 | import base from '../base';
9 |
10 | // Order: state, lifecycle events, custom stuff, render
11 | class App extends React.Component {
12 | // State
13 |
14 | // State property
15 | state = {
16 | fishes: {},
17 | order: {}
18 | };
19 |
20 | static propTypes = {
21 | match: PropTypes.object
22 | // match: this.propTypes.object
23 | }
24 |
25 | componentDidMount() {
26 | const { params } = this.props.match;
27 |
28 | // 1. Reinstate localStorage
29 | const localStorageRef = localStorage.getItem(params.storeId);
30 | if (localStorageRef) {
31 | this.setState({ order: JSON.parse(localStorageRef) });
32 | }
33 |
34 | // In Firebase, refs are references to data
35 | this.ref = base.syncState(`${params.storeId}/fishes`, {
36 | context: this,
37 | state: 'fishes'
38 | });
39 | }
40 |
41 | componentDidUpdate() {
42 | localStorage.setItem(this.props.match.params.storeId, JSON.stringify(this.state.order));
43 | }
44 |
45 | componentWillUnmount() {
46 | base.removeBinding(this.ref);
47 | }
48 |
49 | // State functions - must live inside same component that state lives in
50 |
51 | // Update state
52 | addFish = fish => {
53 | // 1. Copy existing state, never want to directly modify state (mutation)
54 | const fishes = { ...this.state.fishes };
55 |
56 | // 2. Add new fish to fishes variable
57 | fishes[`fish${Date.now()}`] = fish;
58 |
59 | // 3. Set the new fishes object to state
60 | // this.setState({ fishes: fishes });
61 | this.setState({ fishes });
62 | };
63 |
64 | updateFish = (key, updatedFish) => {
65 | // 1. Copy current state
66 | const fishes = { ...this.state.fishes };
67 |
68 | // 2. Update state
69 | fishes[key] = updatedFish;
70 |
71 | // 3. Set that to state
72 | this.setState({ fishes });
73 | }
74 |
75 | deleteFish = key => {
76 | // 1. Copy current state
77 | const fishes = { ...this.state.fishes };
78 |
79 | // 2. Update state
80 | fishes[key] = null;
81 |
82 | // 3. Set that to state
83 | this.setState({ fishes });
84 | }
85 |
86 | // Load sample data
87 | loadSampleFishes = () => {
88 | this.setState({ fishes: sampleFishes });
89 | }
90 |
91 | addToOrder = key => {
92 | // 1. Copy existing state
93 | const order = { ...this.state.order };
94 |
95 | // 2. Either add to order or update number in order
96 | order[key] = order[key] + 1 || 1;
97 |
98 | // 3. Update state object
99 | this.setState({ order });
100 | }
101 |
102 | removeFromOrder = key => {
103 | // 1. Copy existing state
104 | const order = { ...this.state.order };
105 |
106 | // 2. Remove from order
107 | delete order[key];
108 |
109 | // 3. Update state object
110 | this.setState({ order });
111 | }
112 |
113 | render() {
114 | return (
115 |
116 |
117 |
118 |
119 | {Object.keys(this.state.fishes).map(key =>
120 |
126 | )}
127 |
128 |
129 |
134 |
142 |
143 | );
144 | }
145 | }
146 |
147 | export default App;
--------------------------------------------------------------------------------
/src/css/style.styl:
--------------------------------------------------------------------------------
1 | // Import all partials
2 | @require './_*.styl'
3 |
4 |
5 | header.top
6 | text-align center
7 | h1
8 | font-size 14.4rem
9 | line-height 0.7 // this font has a wacky baseline
10 | display flex
11 | justify-content: center;
12 |
13 | // This is a bunch of goofy CSS to make the logo look decent
14 | .ofThe
15 | display flex
16 | font-size 3rem
17 | color orange
18 | justify-content: center;
19 | align-items: center;
20 | background url('images/anchor.svg') center no-repeat;
21 | background-size cover
22 | padding 0 1rem
23 | .of
24 | padding-right 2rem
25 | position relative
26 | right -0.5rem
27 | h3
28 | margin 0
29 | font-size 2rem
30 | color orange
31 | position relative
32 | display inline-block
33 | span
34 | background white
35 | position relative
36 | z-index 2
37 | padding-left 1rem
38 | padding-right 1rem
39 | &:before, &:after
40 | display block
41 | z-index 1
42 | background black
43 | position absolute
44 | width 130%
45 | height 1px
46 | content ''
47 | top 5px
48 | margin-left -15%
49 | &:after
50 | top auto
51 | bottom 7px
52 |
53 |
54 | .catch-of-the-day
55 | display flex
56 | height 90vh
57 | max-width:1500px
58 | margin 0 auto
59 | margin-top 5vh
60 | perspective: 1000px;
61 | transform-style preserve-3d
62 | & > *
63 | flex 1 4 auto
64 | padding 2rem
65 | border 1rem double lighten(black,10%)
66 | position relative
67 | background white
68 | transition all 0.3s
69 | box-shadow 0 5px 5px rgba(0,0,0,0.1)
70 | overflow scroll
71 | &:first-child
72 | flex-shrink 1 // take 4x the extra room
73 | flex-basis 50%
74 | transform translateX(50%) rotateY(6deg) translateX(-50%)
75 | &:nth-child(2)
76 | transform translateX(-50%) rotateY(-14deg) translateX(50%)
77 | border-left 0
78 | border-right 0
79 | min-width 300px
80 | &:last-child
81 | flex-shrink 1 // take 4x the extra room
82 | flex-basis 50%
83 | transform translateX(-50%) rotateY(10deg) translateX(50%) scale(1.08) translateX(24px)
84 |
85 | // Folding Transforms
86 | // Take off folding when not checked
87 | input#fold:not(:checked) ~ #main
88 | .catch-of-the-day > *
89 | transform none
90 |
91 | label[for="fold"]
92 | position absolute
93 | top 1rem
94 | left 1rem
95 | text-transform uppercase
96 | font-size 1.3rem
97 | background black
98 | color white
99 | border 2px solid black
100 | cursor pointer
101 | padding 0.5rem 1rem
102 |
103 | input#fold
104 | display none
105 | &:checked + label
106 | background white
107 | color black
108 |
109 | ul
110 | list-style none
111 | margin 0
112 | padding 0
113 |
114 | ul.order
115 | // Default state
116 | li
117 | border-bottom 1px solid black
118 | padding 2rem 0
119 | display flex
120 | font-size 1.4rem
121 | justify-content space-between
122 | align-items center
123 | &:hover
124 | // padding 1rem 0
125 | button
126 | display inline
127 | button
128 | border 0
129 | display none
130 | line-height 1
131 | padding 0
132 | &.unavailable
133 | text-decoration line-through
134 | background lighten(red, 80%)
135 | .price
136 | font-size 1.2rem
137 | span.count
138 | position relative
139 | overflow hidden
140 | float left // only works if it's floated?!
141 | span
142 | display inline-block
143 | // transition all 0.5s
144 | .total
145 | padding 2rem 0
146 | font-size 1.4rem
147 | border-bottom 3px solid black
148 | border-top 3px double black
149 | & strong
150 | float: right
151 |
152 | .order-title
153 | text-align center
154 |
155 | .fish-edit
156 | margin-bottom 20px
157 | border 2px solid black
158 | overflow hidden
159 | display flex
160 | flex-wrap wrap
161 | input, textarea, select
162 | width 33.33%
163 | padding 10px
164 | line-height 1
165 | font-size 1.2rem
166 | border 0
167 | border-bottom 1px solid black
168 | border-right 1px solid black
169 | appearance none
170 | border-radius 0
171 | background white
172 | &:focus
173 | outline 0
174 | background lighten(orange, 85%)
175 | textarea
176 | width 100%
177 | input:last-of-type
178 | width 100%
179 | button
180 | width 100%
181 | border 0
182 |
183 |
184 | // Menu Styles
185 | .list-of-fish
186 | border-top 2px solid black
187 | border-bottom 1px solid black
188 | padding-top 5px
189 | margin-top 2rem
190 | transform translateZ(0);
191 |
192 | .menu-fish
193 | border-bottom 2px solid black
194 | border-top 1px solid black
195 | padding-bottom 2rem
196 | padding-top 2rem
197 | margin-bottom 5px
198 | clear both
199 | overflow hidden
200 | p
201 | margin 0
202 | font-size 1.8rem
203 | .fish-name
204 | margin 0
205 | display flex
206 | justify-content space-between
207 | align-items center
208 | .price
209 | font-size 1.4rem
210 | // color orange
211 | justify-content flex-end
212 | // font-family 'Open Sans Condensed'
213 | img
214 | float left
215 | width 150px
216 | margin-right 1rem
217 |
218 | button, input[type=submit]
219 | text-transform uppercase
220 | background none
221 | border 1px solid black
222 | font-weight 600
223 | font-size 1.5rem
224 | font-family 'Open Sans', sans-serif
225 | transition all 0.2s
226 | position relative
227 | z-index 2
228 | &[disabled]
229 | color red
230 | background white
231 | border-color red
232 | transform rotate(-10deg) scale(2) translateX(50%) translateY(-50%)
233 | &:hover
234 | color red
235 | cursor not-allowed
236 | &:after
237 | display none
238 | &:after
239 | content ''
240 | z-index -1
241 | display block
242 | background black
243 | position absolute
244 | width 100%
245 | height 0
246 | left 0
247 | top 0
248 | transition all 0.2s
249 | &:hover, &:focus
250 | color white
251 | outline 0
252 | &:after
253 | height 100%
254 | // variants
255 | &.warning
256 | &:after
257 | background red
258 | &.success
259 | &:after
260 | background green
261 |
262 | &.github, &.facebook, &.twitter
263 | border 0
264 | display block
265 | margin-bottom 2rem
266 | width 100%
267 | color white
268 | padding 2rem
269 |
270 | &.github
271 | background #82D465
272 | &:after
273 | background darken(#82D465, 20%)
274 |
275 | &.facebook
276 | background #3864A3
277 | &:after
278 | background darken(#3864A3, 20%)
279 |
280 | &.twitter
281 | background #5EA9DD
282 | &:after
283 | background darken(#5EA9DD, 20%)
284 |
285 |
286 | // Store Selector
287 | .store-selector
288 | background white
289 | max-width 500px
290 | margin 50px auto
291 | padding 2rem
292 | border 2px solid black
293 | input, button
294 | width 100%
295 | &[type="text"]
296 | text-align center
297 | font-size 3rem
298 |
--------------------------------------------------------------------------------
/src/css/fonts/haymaker-webfont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/css/style.css:
--------------------------------------------------------------------------------
1 | .order-enter {
2 | -webkit-transform: translateX(-120%);
3 | transform: translateX(-120%);
4 | -webkit-transition: 0.5s;
5 | transition: 0.5s;
6 | max-height: 0;
7 | padding: 0 !important;
8 | }
9 | .order-enter.order-enter-active {
10 | max-height: 60px;
11 | -webkit-transform: translateX(0);
12 | transform: translateX(0);
13 | padding: 2rem 0 !important;
14 | }
15 | .order-exit {
16 | -webkit-transition: 0.5s;
17 | transition: 0.5s;
18 | -webkit-transform: translateX(0);
19 | transform: translateX(0);
20 | }
21 | .order-exit.order-exit-active {
22 | -webkit-transform: translateX(120%);
23 | transform: translateX(120%);
24 | padding: 0;
25 | }
26 | .count-enter {
27 | background: #f00;
28 | -webkit-transition: 0.5s;
29 | transition: 0.5s;
30 | -webkit-transform: translateY(100%);
31 | transform: translateY(100%);
32 | }
33 | .count-enter.count-enter-active {
34 | background: #ff0;
35 | -webkit-transform: translateY(0);
36 | transform: translateY(0);
37 | }
38 | .count-exit {
39 | background: #000;
40 | -webkit-transform: translateY(0);
41 | transform: translateY(0);
42 | -webkit-transition: 0.5s;
43 | transition: 0.5s;
44 | position: absolute;
45 | left: 0;
46 | bottom: 0;
47 | }
48 | .count-exit.count-exit-active {
49 | background: #008000;
50 | -webkit-transform: translateY(-100%) scale(3);
51 | transform: translateY(-100%) scale(3);
52 | }
53 | @font-face {
54 | font-family: "haymakerregular";
55 | src: url("./fonts/haymaker-webfont.eot");
56 | src: url("./fonts/haymaker-webfont.eot?#iefix") format("embedded-opentype"), url("./fonts/haymaker-webfont.woff") format("woff"), url("./fonts/haymaker-webfont.ttf") format("truetype"), url("./fonts/haymaker-webfont.svg#haymakerregular") format("svg");
57 | font-weight: normal;
58 | font-style: normal;
59 | }
60 | @font-face {
61 | font-family: 'blanchcaps_inline';
62 | src: url("./fonts/blanch_caps_inline-webfont.eot");
63 | src: url("./fonts/blanch_caps_inline-webfont.eot?#iefix") format('embedded-opentype'), url("./fonts/blanch_caps_inline-webfont.woff") format('woff'), url("./fonts/blanch_caps_inline-webfont.ttf") format('truetype'), url("./fonts/blanch_caps_inline-webfont.svg#blanchcaps_inline") format('svg');
64 | font-weight: normal;
65 | font-style: normal;
66 | }
67 | article,
68 | aside,
69 | details,
70 | figcaption,
71 | figure,
72 | footer,
73 | header,
74 | hgroup,
75 | nav,
76 | section,
77 | summary {
78 | display: block;
79 | }
80 | audio,
81 | canvas,
82 | video {
83 | display: inline-block;
84 | }
85 | audio:not([controls]) {
86 | display: none;
87 | height: 0;
88 | }
89 | [hidden] {
90 | display: none;
91 | }
92 | html {
93 | font-family: sans-serif;
94 | -webkit-text-size-adjust: 100%;
95 | -ms-text-size-adjust: 100%;
96 | }
97 | a:focus {
98 | outline: thin dotted;
99 | }
100 | a:active,
101 | a:hover {
102 | outline: 0;
103 | }
104 | h1 {
105 | font-size: 2em;
106 | }
107 | abbr[title] {
108 | border-bottom: 1px dotted;
109 | }
110 | b,
111 | strong {
112 | font-weight: 700;
113 | }
114 | dfn {
115 | font-style: italic;
116 | }
117 | mark {
118 | background: #ff0;
119 | color: #000;
120 | }
121 | code,
122 | kbd,
123 | pre,
124 | samp {
125 | font-family: monospace, serif;
126 | font-size: 1em;
127 | }
128 | pre {
129 | white-space: pre-wrap;
130 | word-wrap: break-word;
131 | }
132 | q {
133 | quotes: 2 1C 2 1D 2 18 2 19;
134 | }
135 | small {
136 | font-size: 80%;
137 | }
138 | sub,
139 | sup {
140 | font-size: 75%;
141 | line-height: 0;
142 | position: relative;
143 | vertical-align: baseline;
144 | }
145 | sup {
146 | top: -0.5em;
147 | }
148 | sub {
149 | bottom: -0.25em;
150 | }
151 | img {
152 | border: 0;
153 | }
154 | svg:not(:root) {
155 | overflow: hidden;
156 | }
157 | fieldset {
158 | border: 1px solid #c0c0c0;
159 | margin: 0 2px;
160 | padding: 0.35em 0.625em 0.75em;
161 | }
162 | button,
163 | input,
164 | select,
165 | textarea {
166 | font-family: inherit;
167 | font-size: 100%;
168 | margin: 0;
169 | }
170 | button,
171 | input {
172 | line-height: normal;
173 | }
174 | button,
175 | html input[type=button],
176 | input[type=reset],
177 | input[type=submit] {
178 | -webkit-appearance: button;
179 | cursor: pointer;
180 | }
181 | button[disabled],
182 | input[disabled] {
183 | cursor: default;
184 | }
185 | input[type=checkbox],
186 | input[type=radio] {
187 | box-sizing: border-box;
188 | padding: 0;
189 | }
190 | input[type=search] {
191 | -webkit-appearance: textfield;
192 | box-sizing: content-box;
193 | }
194 | input[type=search]::-webkit-search-cancel-button,
195 | input[type=search]::-webkit-search-decoration {
196 | -webkit-appearance: none;
197 | }
198 | textarea {
199 | overflow: auto;
200 | vertical-align: top;
201 | }
202 | table {
203 | border-collapse: collapse;
204 | border-spacing: 0;
205 | }
206 | body,
207 | figure {
208 | margin: 0;
209 | }
210 | legend,
211 | button::-moz-focus-inner,
212 | input::-moz-focus-inner {
213 | border: 0;
214 | padding: 0;
215 | }
216 | .clearfix:after {
217 | visibility: hidden;
218 | display: block;
219 | font-size: 0;
220 | content: " ";
221 | clear: both;
222 | height: 0;
223 | }
224 | * {
225 | box-sizing: border-box;
226 | }
227 | html {
228 | font-size: 62.5%;
229 | }
230 | body {
231 | background: #d4d4d4;
232 | -webkit-font-smoothing: antialiased;
233 | -moz-osx-font-smoothing: grayscale;
234 | font-family: 'Open Sans', sans-serif;
235 | font-size: 2rem;
236 | }
237 | h1 {
238 | font-family: 'blanchcaps_inline', sans-serif;
239 | text-align: center;
240 | font-weight: normal;
241 | margin: 0;
242 | }
243 | h2,
244 | h3,
245 | h4,
246 | h5,
247 | h6 {
248 | font-weight: normal;
249 | font-family: 'haymakerregular', sans-serif;
250 | }
251 | h2 {
252 | text-align: center;
253 | margin-top: 0;
254 | margin-bottom: 2rem;
255 | }
256 | h3 {
257 | font-size: 3rem;
258 | }
259 | header.top {
260 | text-align: center;
261 | }
262 | header.top h1 {
263 | font-size: 14.4rem;
264 | line-height: 0.7;
265 | display: -webkit-box;
266 | display: flex;
267 | -webkit-box-pack: center;
268 | justify-content: center;
269 | }
270 | header.top h1 .ofThe {
271 | display: -webkit-box;
272 | display: flex;
273 | font-size: 3rem;
274 | color: #f5a623;
275 | -webkit-box-pack: center;
276 | justify-content: center;
277 | -webkit-box-align: center;
278 | align-items: center;
279 | background: url("images/anchor.svg") center no-repeat;
280 | background-size: cover;
281 | padding: 0 1rem;
282 | }
283 | header.top h1 .ofThe .of {
284 | padding-right: 2rem;
285 | position: relative;
286 | right: -0.5rem;
287 | }
288 | header.top h3 {
289 | margin: 0;
290 | font-size: 2rem;
291 | color: #f5a623;
292 | position: relative;
293 | display: inline-block;
294 | }
295 | header.top h3 span {
296 | background: #fff;
297 | position: relative;
298 | z-index: 2;
299 | padding-left: 1rem;
300 | padding-right: 1rem;
301 | }
302 | header.top h3:before,
303 | header.top h3:after {
304 | display: block;
305 | z-index: 1;
306 | background: #000;
307 | position: absolute;
308 | width: 130%;
309 | height: 1px;
310 | content: '';
311 | top: 5px;
312 | margin-left: -15%;
313 | }
314 | header.top h3:after {
315 | top: auto;
316 | bottom: 7px;
317 | }
318 | .catch-of-the-day {
319 | display: -webkit-box;
320 | display: flex;
321 | height: 90vh;
322 | max-width: 1500px;
323 | margin: 0 auto;
324 | margin-top: 5vh;
325 | -webkit-perspective: 1000px;
326 | perspective: 1000px;
327 | -webkit-transform-style: preserve-3d;
328 | transform-style: preserve-3d;
329 | }
330 | .catch-of-the-day > * {
331 | -webkit-box-flex: 1;
332 | flex: 1 4 auto;
333 | padding: 2rem;
334 | border: 1rem double #1a1a1a;
335 | position: relative;
336 | background: #fff;
337 | -webkit-transition: all 0.3s;
338 | transition: all 0.3s;
339 | box-shadow: 0 5px 5px rgba(0,0,0,0.1);
340 | overflow: scroll;
341 | }
342 | .catch-of-the-day > *:first-child {
343 | flex-shrink: 1;
344 | flex-basis: 50%;
345 | -webkit-transform: translateX(50%) rotateY(6deg) translateX(-50%);
346 | transform: translateX(50%) rotateY(6deg) translateX(-50%);
347 | }
348 | .catch-of-the-day > *:nth-child(2) {
349 | -webkit-transform: translateX(-50%) rotateY(-14deg) translateX(50%);
350 | transform: translateX(-50%) rotateY(-14deg) translateX(50%);
351 | border-left: 0;
352 | border-right: 0;
353 | min-width: 300px;
354 | }
355 | .catch-of-the-day > *:last-child {
356 | flex-shrink: 1;
357 | flex-basis: 50%;
358 | -webkit-transform: translateX(-50%) rotateY(10deg) translateX(50%) scale(1.08) translateX(24px);
359 | transform: translateX(-50%) rotateY(10deg) translateX(50%) scale(1.08) translateX(24px);
360 | }
361 | input#fold:not(:checked) ~ #main .catch-of-the-day > * {
362 | -webkit-transform: none;
363 | transform: none;
364 | }
365 | label[for="fold"] {
366 | position: absolute;
367 | top: 1rem;
368 | left: 1rem;
369 | text-transform: uppercase;
370 | font-size: 1.3rem;
371 | background: #000;
372 | color: #fff;
373 | border: 2px solid #000;
374 | cursor: pointer;
375 | padding: 0.5rem 1rem;
376 | }
377 | input#fold {
378 | display: none;
379 | }
380 | input#fold:checked + label {
381 | background: #fff;
382 | color: #000;
383 | }
384 | ul {
385 | list-style: none;
386 | margin: 0;
387 | padding: 0;
388 | }
389 | ul.order li {
390 | border-bottom: 1px solid #000;
391 | padding: 2rem 0;
392 | display: -webkit-box;
393 | display: flex;
394 | font-size: 1.4rem;
395 | -webkit-box-pack: justify;
396 | justify-content: space-between;
397 | -webkit-box-align: center;
398 | align-items: center;
399 | }
400 | ul.order li:hover button {
401 | display: inline;
402 | }
403 | ul.order li button {
404 | border: 0;
405 | display: none;
406 | line-height: 1;
407 | padding: 0;
408 | }
409 | ul.order li.unavailable {
410 | text-decoration: line-through;
411 | background: #f8d0d2;
412 | }
413 | ul.order li .price {
414 | font-size: 1.2rem;
415 | }
416 | ul.order li span.count {
417 | position: relative;
418 | overflow: hidden;
419 | float: left;
420 | }
421 | ul.order li span.count span {
422 | display: inline-block;
423 | }
424 | .total {
425 | padding: 2rem 0;
426 | font-size: 1.4rem;
427 | border-bottom: 3px solid #000;
428 | border-top: 3px double #000;
429 | }
430 | .total strong {
431 | float: right;
432 | }
433 | .order-title {
434 | text-align: center;
435 | }
436 | .fish-edit {
437 | margin-bottom: 20px;
438 | border: 2px solid #000;
439 | overflow: hidden;
440 | display: -webkit-box;
441 | display: flex;
442 | flex-wrap: wrap;
443 | }
444 | .fish-edit input,
445 | .fish-edit textarea,
446 | .fish-edit select {
447 | width: 33.33%;
448 | padding: 10px;
449 | line-height: 1;
450 | font-size: 1.2rem;
451 | border: 0;
452 | border-bottom: 1px solid #000;
453 | border-right: 1px solid #000;
454 | -webkit-appearance: none;
455 | -moz-appearance: none;
456 | appearance: none;
457 | border-radius: 0;
458 | background: #fff;
459 | }
460 | .fish-edit input:focus,
461 | .fish-edit textarea:focus,
462 | .fish-edit select:focus {
463 | outline: 0;
464 | background: #fef2de;
465 | }
466 | .fish-edit textarea {
467 | width: 100%;
468 | }
469 | .fish-edit input:last-of-type {
470 | width: 100%;
471 | }
472 | .fish-edit button {
473 | width: 100%;
474 | border: 0;
475 | }
476 | .list-of-fish {
477 | border-top: 2px solid #000;
478 | border-bottom: 1px solid #000;
479 | padding-top: 5px;
480 | margin-top: 2rem;
481 | -webkit-transform: translateZ(0);
482 | transform: translateZ(0);
483 | }
484 | .menu-fish {
485 | border-bottom: 2px solid #000;
486 | border-top: 1px solid #000;
487 | padding-bottom: 2rem;
488 | padding-top: 2rem;
489 | margin-bottom: 5px;
490 | clear: both;
491 | overflow: hidden;
492 | }
493 | .menu-fish p {
494 | margin: 0;
495 | font-size: 1.8rem;
496 | }
497 | .menu-fish .fish-name {
498 | margin: 0;
499 | display: -webkit-box;
500 | display: flex;
501 | -webkit-box-pack: justify;
502 | justify-content: space-between;
503 | -webkit-box-align: center;
504 | align-items: center;
505 | }
506 | .menu-fish .price {
507 | font-size: 1.4rem;
508 | -webkit-box-pack: end;
509 | justify-content: flex-end;
510 | }
511 | .menu-fish img {
512 | float: left;
513 | width: 150px;
514 | margin-right: 1rem;
515 | }
516 | button,
517 | input[type=submit] {
518 | text-transform: uppercase;
519 | background: none;
520 | border: 1px solid #000;
521 | font-weight: 600;
522 | font-size: 1.5rem;
523 | font-family: 'Open Sans';
524 | -webkit-transition: all 0.2s;
525 | transition: all 0.2s;
526 | position: relative;
527 | z-index: 2;
528 | }
529 | button[disabled],
530 | input[type=submit][disabled] {
531 | color: #d12028;
532 | background: #fff;
533 | border-color: #d12028;
534 | -webkit-transform: rotate(-10deg) scale(2) translateX(50%) translateY(-50%);
535 | transform: rotate(-10deg) scale(2) translateX(50%) translateY(-50%);
536 | }
537 | button[disabled]:hover,
538 | input[type=submit][disabled]:hover {
539 | color: #d12028;
540 | cursor: not-allowed;
541 | }
542 | button[disabled]:after,
543 | input[type=submit][disabled]:after {
544 | display: none;
545 | }
546 | button:after,
547 | input[type=submit]:after {
548 | content: '';
549 | z-index: -1;
550 | display: block;
551 | background: #000;
552 | position: absolute;
553 | width: 100%;
554 | height: 0;
555 | left: 0;
556 | top: 0;
557 | -webkit-transition: all 0.2s;
558 | transition: all 0.2s;
559 | }
560 | button:hover,
561 | input[type=submit]:hover,
562 | button:focus,
563 | input[type=submit]:focus {
564 | color: #fff;
565 | outline: 0;
566 | }
567 | button:hover:after,
568 | input[type=submit]:hover:after,
569 | button:focus:after,
570 | input[type=submit]:focus:after {
571 | height: 100%;
572 | }
573 | button.warning:after,
574 | input[type=submit].warning:after {
575 | background: #d12028;
576 | }
577 | button.success:after,
578 | input[type=submit].success:after {
579 | background: #2dc22d;
580 | }
581 | button.github,
582 | input[type=submit].github,
583 | button.facebook,
584 | input[type=submit].facebook,
585 | button.twitter,
586 | input[type=submit].twitter {
587 | border: 0;
588 | display: block;
589 | margin-bottom: 2rem;
590 | width: 100%;
591 | color: #fff;
592 | padding: 2rem;
593 | }
594 | button.github,
595 | input[type=submit].github {
596 | background: #82d465;
597 | }
598 | button.github:after,
599 | input[type=submit].github:after {
600 | background: #5cc437;
601 | }
602 | button.facebook,
603 | input[type=submit].facebook {
604 | background: #3864a3;
605 | }
606 | button.facebook:after,
607 | input[type=submit].facebook:after {
608 | background: #2d5082;
609 | }
610 | button.twitter,
611 | input[type=submit].twitter {
612 | background: #5ea9dd;
613 | }
614 | button.twitter:after,
615 | input[type=submit].twitter:after {
616 | background: #2c8dd0;
617 | }
618 | .store-selector {
619 | background: #fff;
620 | max-width: 500px;
621 | margin: 50px auto;
622 | padding: 2rem;
623 | border: 2px solid #000;
624 | }
625 | .store-selector input,
626 | .store-selector button {
627 | width: 100%;
628 | }
629 | .store-selector input[type="text"],
630 | .store-selector button[type="text"] {
631 | text-align: center;
632 | font-size: 3rem;
633 | }
634 |
--------------------------------------------------------------------------------
/src/css/fonts/blanch_caps_inline-webfont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------