File size: 8,246 Bytes
88bf7e3
8402a8c
88bf7e3
8402a8c
addbb97
 
 
 
8402a8c
 
cbd8a2b
8402a8c
 
 
88bf7e3
8402a8c
 
8abff3a
 
 
 
 
 
 
 
51814f6
8402a8c
addbb97
e84b596
 
 
 
 
 
 
 
 
 
 
 
 
addbb97
e84b596
 
 
 
 
addbb97
 
 
e84b596
73ab857
 
 
e84b596
 
 
 
 
 
 
 
 
 
 
 
addbb97
8402a8c
 
e84b596
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4b507bf
 
 
 
e84b596
8402a8c
cbd8a2b
 
4664117
 
 
 
 
 
 
cbd8a2b
4664117
 
 
 
 
 
cbd8a2b
 
88bf7e3
 
8402a8c
 
8d65d3c
e84b596
 
8402a8c
 
4b507bf
d8a4ec1
 
 
8402a8c
e84b596
aab4a1f
d8a4ec1
8402a8c
5b946bc
d8a4ec1
5b946bc
 
4b507bf
5b946bc
 
 
 
805d1f4
4366ec6
 
 
 
b070982
e84b596
 
 
 
 
 
88bf7e3
e063967
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
from fasthtml.common import *
# MonsterUI shadows fasthtml components with the same name
from monsterui.all import *
# If you don't want shadowing behavior, you use import monsterui.core as ... style instead
from fasthtml.components import Uk_input_tag
from fasthtml.svg import *
import calendar
from datetime import datetime

# Get frankenui and tailwind headers via CDN using Theme.blue.headers()
hdrs = Theme.blue.headers() 

# fast_app is shadowed by MonsterUI to make it default to no Pico, and add body classes
# needed for frankenui theme styling
app, rt = fast_app(hdrs=hdrs, live=True)

products = [
    {"name": "Kalpasi (Black Stone Flower)", "price": "$509.50", "img": "https://picsum.photos/400/100?random=1"},
    {"name": "Radhuni (Wild Celery Seeds)",  "price": "$505.25", "img": "https://picsum.photos/400/100?random=2"},
    {"name": "Kokum",                        "price": "$503.00", "img": "https://picsum.photos/400/100?random=3"},
    {"name": "Mace (Javitri)",               "price": "$504.50", "img": "https://picsum.photos/400/100?random=4"},
    {"name": "Anardana (Dried Pomegranate)", "price": "$506.25", "img": "https://picsum.photos/400/100?random=5"},
    {"name": "Ajmod (Indian Lovage)",        "price": "$507.00", "img": "https://picsum.photos/400/100?random=6"},
    {"name": "Marathi Moggu",                "price": "$508.50", "img": "https://picsum.photos/400/100?random=7"},
    {"name": "Kalonji (Nigella Seeds)",      "price": "$504.25", "img": "https://picsum.photos/400/100?random=8"}
]

CreateAccount = Card(
    Grid(
        Button(DivLAligned(UkIcon('github'), Div('Github')), cls="w-full"),
        Button('Google', cls="w-full")
    ),
    DividerSplit("OR CONTINUE WITH", text_cls=TextPresets.muted_sm),
    LabelInput('Email',    id='email',   placeholder='m@example.com'),
    LabelInput('Password', id='password',placeholder='Password', type='Password'),
    header=(H3('Create an Account'), Subtitle('Enter your email below to create your account')),
    footer=Button('Create Account', cls=(ButtonT.primary, 'w-full'))
)

PaypalSVG_data = "..."  # unchanged
AppleSVG_data  = "..."  # unchanged

Card1Svg  = Svg(viewBox="0 0 24 24", fill="none", stroke="currentColor", stroke_linecap="round", stroke_linejoin="round", stroke_width="2", cls="h-6 w-6 mr-1")(
    Rect(width="20", height="14", x="2", y="5", rx="2"),
    Path(d="M2 10h20")
)
PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=PaypalSVG_data, fill="currentColor"))
AppleSvg  = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=AppleSVG_data, fill="currentColor"))

PaymentMethod = Card(
    Grid(
        Button(DivCentered(Card1Svg, "Card"), cls='h-20 border-2 border-primary rounded-lg', type="button"),
        Button(DivCentered(PaypalSvg, "PayPal"), cls='h-20 border rounded-lg', type="button"),
        Button(DivCentered(AppleSvg, "Apple"), cls='h-20 border rounded-lg', type="button")
    ),
    Form(
        LabelInput('Name',        id='name',        placeholder='John Doe'),
        LabelInput('Card Number', id='card_number', placeholder='1234 5678 9012 3456'),
        Grid(
            LabelSelect(*Options(*calendar.month_name[1:], selected_idx=0), label='Expires', id='expire_month'),
            LabelSelect(*Options(*range(2024,2030), selected_idx=0),       label='Year',    id='expire_year'),
            LabelInput('CVV', id='cvv', placeholder='CVV', cls='mt-0')
        )
    ),
    header=(H3('Payment Method'), Subtitle('Add a new payment method to your account.'))
)

def ProductCard(p):
    # Card does lots of boilerplate classes so you can just pass in the content
    return Card(
        # width:100% makes the image take the full width so we are guarenteed that we won't
        # have the image cut off or not large enough. Because all our images are a consistent
        # size we do not need to worry about stretching or skewing the image, this is ideal.
        # If you have images of different sizes, you will need to use object-fit:cover and/or
        # height to either strech, shrink, or crop the image. It is much better to adjust your
        # images to be a consistent size upfront so you don't have to handle edge cases of
        # different images skeweing/stretching differently.
        Img(src=p["img"], alt=p["name"], cls="w-full h-40 object-cover rounded-t-lg"),
        # All components can take a cls argument to add additional styling - `mt-2` adds margin
        # to the top (see spacing tutorial for details on spacing).
        # 
        # Often adding space makes a site look more put together - usually the 2 - 5 range is a
        # good choice
        H4(p["name"], cls="mt-3 text-lg font-semibold text-gray-900"),
        # There are helpful Enums, such as TextPresetsT, ButtonT, ContainerT, etc that allow for easy
        # discoverability of class options.
        # bold_sm is helpful for things that you want to look like regular text, but stand out
        # visually for emphasis.
        P(p["price"], cls=(TextPresets.bold_sm, "text-gray-700 mt-1")),
        # ButtonT.primary is useful for actions you really want the user to take (like adding
        # something to the cart) - these stand out visually. For dangerous actions (like
        # deleting something) you generally would want to use ButtonT.destructive. For UX actions
        # that aren't a goal of the page (like cancelling something that hasn't been submitted)
        # you generally want the default styling.
        Button("Add to cart", cls=(ButtonT.primary, "mt-3 w-full"),
           hx_get=product_detail.to(product_name=p['name'], product_price=p['price']),
           hx_push_url='true',
           hx_target='body'),
           cls="shadow-lg rounded-lg overflow-hidden bg-white hover:shadow-xl transition-shadow"
    )

# Define this once, at module level (top of file is best)
example_product_description = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
Praesent euismod, sapien nec facilisis tincidunt, nunc 
nibh posuere justo, vitae luctus neque magna vel nulla. 
Curabitur at felis ac nulla fermentum tincidunt. 
Integer non risus nec nulla cursus porttitor. 
Suspendisse potenti. Donec vel sapien nec erat 
malesuada viverra sed a lorem. 

- Proin facilisis ligula sed sapien tincidunt, at 
  fermentum magna volutpat.  
- Curabitur vitae lectus nec justo cursus 
  sollicitudin non sed est.  
- Sed ut perspiciatis unde omnis iste natus error 
  sit voluptatem accusantium doloremque laudantium.  
"""

@rt
def index():
    # Titled using a H1 title, sets the page title, and wraps contents in Main(Container(...)) using
    # frankenui styles. Generally you will want to use Titled for all of your pages
    return Titled("Store",
        Grid(*[ProductCard(p) for p in products], cols_lg=3, gap=6, cls="p-6")
    )

@rt
def product_detail(product_name: str, product_price: str):
    return (
        Title("Product Detail"),
        Grid(
            Div(
                H3(product_name, cls="text-xl font-semibold mb-4"),
                render_md(example_product_description.format(product_name=product_name))
            ),
            Div(
                H3("Order", cls="text-lg font-semibold mb-3"),
                Form(
                    LabelInput("Product", id="name", value=product_name, readonly=True,
                               cls="bg-gray-100 cursor-not-allowed"),
                    LabelInput("Price", id="price", value=product_price, readonly=True,
                               cls="bg-gray-100 cursor-not-allowed"),
                    LabelInput("Quantity", id="quantity", placeholder="1"),
                    LabelInput("Email", id="email", placeholder="accountemail@example.com"),
                    Div(PaymentMethod, cls="space-y-4 mt-4"),
                    Button("Add to order", cls=(ButtonT.primary, "w-full mt-4")),
                    Button("Continue shopping",
                           cls=(ButtonT.secondary, "w-full mt-2"),
                           hx_get="/",
                           hx_push_url="true",
                           hx_target="body")
                )
            ),
            cols_lg=2,
            cls="gap-8 p-6"
        )
    )

serve()