Jofax commited on
Commit
f04fea5
·
verified ·
1 Parent(s): 467e9b5

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -761
app.py DELETED
@@ -1,761 +0,0 @@
1
- import gradio as gr
2
- import pandas as pd
3
- from datetime import datetime
4
-
5
- # --- LUXURY CUSTOM CSS ---
6
- custom_css = """
7
- @import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@300;400;600;700&family=Montserrat:wght@300;400;500;600&display=swap');
8
-
9
- :root {
10
- --gold: #D4AF37;
11
- --gold-dark: #B8941F;
12
- --navy: #0A1828;
13
- --navy-light: #1A2F42;
14
- --cream: #F8F6F0;
15
- --white: #FFFFFF;
16
- --shadow: rgba(10, 24, 40, 0.15);
17
- --shadow-heavy: rgba(10, 24, 40, 0.3);
18
- }
19
-
20
- .gradio-container {
21
- background: linear-gradient(135deg, #0A1828 0%, #1A2F42 50%, #0A1828 100%) !important;
22
- font-family: 'Montserrat', sans-serif !important;
23
- position: relative;
24
- overflow-x: hidden;
25
- }
26
-
27
- .gradio-container::before {
28
- content: '';
29
- position: fixed;
30
- top: 0;
31
- left: 0;
32
- right: 0;
33
- bottom: 0;
34
- background-image:
35
- radial-gradient(circle at 20% 30%, rgba(212, 175, 55, 0.05) 0%, transparent 50%),
36
- radial-gradient(circle at 80% 70%, rgba(212, 175, 55, 0.03) 0%, transparent 50%);
37
- pointer-events: none;
38
- z-index: 0;
39
- }
40
-
41
- /* Header Styling */
42
- .luxury-header {
43
- text-align: center;
44
- padding: 60px 20px 40px;
45
- position: relative;
46
- z-index: 1;
47
- }
48
-
49
- .luxury-header h1 {
50
- font-family: 'Cormorant Garamond', serif !important;
51
- font-size: 4.5em !important;
52
- font-weight: 300 !important;
53
- color: var(--cream) !important;
54
- margin: 0 0 15px 0 !important;
55
- letter-spacing: 3px !important;
56
- text-transform: uppercase;
57
- animation: fadeInDown 1s ease-out;
58
- }
59
-
60
- .luxury-header .subtitle {
61
- font-size: 1.1em;
62
- color: var(--gold);
63
- letter-spacing: 4px;
64
- text-transform: uppercase;
65
- font-weight: 300;
66
- margin-top: 10px;
67
- animation: fadeInUp 1s ease-out 0.3s both;
68
- }
69
-
70
- .luxury-header .divider {
71
- width: 100px;
72
- height: 2px;
73
- background: linear-gradient(90deg, transparent, var(--gold), transparent);
74
- margin: 25px auto;
75
- animation: expandWidth 1.2s ease-out 0.5s both;
76
- }
77
-
78
- @keyframes fadeInDown {
79
- from {
80
- opacity: 0;
81
- transform: translateY(-30px);
82
- }
83
- to {
84
- opacity: 1;
85
- transform: translateY(0);
86
- }
87
- }
88
-
89
- @keyframes fadeInUp {
90
- from {
91
- opacity: 0;
92
- transform: translateY(30px);
93
- }
94
- to {
95
- opacity: 1;
96
- transform: translateY(0);
97
- }
98
- }
99
-
100
- @keyframes expandWidth {
101
- from {
102
- width: 0;
103
- }
104
- to {
105
- width: 100px;
106
- }
107
- }
108
-
109
- /* Promotion Banner */
110
- .promo-banner {
111
- background: linear-gradient(135deg, var(--gold-dark) 0%, var(--gold) 100%);
112
- color: var(--navy);
113
- padding: 35px;
114
- border-radius: 0;
115
- margin: 30px 0;
116
- text-align: center;
117
- position: relative;
118
- overflow: hidden;
119
- box-shadow: 0 10px 40px var(--shadow-heavy);
120
- animation: slideInFromTop 0.8s ease-out;
121
- }
122
-
123
- .promo-banner::before {
124
- content: '';
125
- position: absolute;
126
- top: -50%;
127
- left: -50%;
128
- width: 200%;
129
- height: 200%;
130
- background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.15) 50%, transparent 70%);
131
- animation: shimmer 3s infinite;
132
- }
133
-
134
- @keyframes shimmer {
135
- 0% {
136
- transform: translateX(-100%) translateY(-100%) rotate(45deg);
137
- }
138
- 100% {
139
- transform: translateX(100%) translateY(100%) rotate(45deg);
140
- }
141
- }
142
-
143
- @keyframes slideInFromTop {
144
- from {
145
- opacity: 0;
146
- transform: translateY(-50px);
147
- }
148
- to {
149
- opacity: 1;
150
- transform: translateY(0);
151
- }
152
- }
153
-
154
- .promo-banner .promo-title {
155
- font-family: 'Cormorant Garamond', serif;
156
- font-size: 1.4em;
157
- font-weight: 600;
158
- letter-spacing: 2px;
159
- text-transform: uppercase;
160
- margin-bottom: 10px;
161
- }
162
-
163
- .promo-banner .promo-message {
164
- font-size: 2em;
165
- font-weight: 600;
166
- margin: 15px 0;
167
- font-family: 'Cormorant Garamond', serif;
168
- }
169
-
170
- .promo-banner .promo-time {
171
- font-size: 0.85em;
172
- opacity: 0.8;
173
- letter-spacing: 1px;
174
- text-transform: uppercase;
175
- }
176
-
177
- /* Tab Styling */
178
- .tabitem {
179
- background: rgba(248, 246, 240, 0.05) !important;
180
- border: none !important;
181
- border-radius: 15px !important;
182
- padding: 40px !important;
183
- margin-top: 20px !important;
184
- backdrop-filter: blur(10px);
185
- }
186
-
187
- button.selected {
188
- background: var(--gold) !important;
189
- color: var(--navy) !important;
190
- font-weight: 600 !important;
191
- border: none !important;
192
- letter-spacing: 1px !important;
193
- }
194
-
195
- .tabs button {
196
- color: var(--cream) !important;
197
- font-size: 1em !important;
198
- padding: 15px 35px !important;
199
- border: 1px solid rgba(212, 175, 55, 0.3) !important;
200
- background: transparent !important;
201
- transition: all 0.3s ease !important;
202
- text-transform: uppercase;
203
- letter-spacing: 1.5px;
204
- font-weight: 500;
205
- }
206
-
207
- .tabs button:hover {
208
- background: rgba(212, 175, 55, 0.1) !important;
209
- border-color: var(--gold) !important;
210
- }
211
-
212
- /* Hotel Cards - Luxury Table Styling */
213
- .dataframe {
214
- background: var(--cream) !important;
215
- border-radius: 15px !important;
216
- overflow: hidden !important;
217
- box-shadow: 0 15px 50px var(--shadow-heavy) !important;
218
- font-family: 'Montserrat', sans-serif !important;
219
- border: 1px solid rgba(212, 175, 55, 0.2) !important;
220
- }
221
-
222
- .dataframe thead {
223
- background: linear-gradient(135deg, var(--navy) 0%, var(--navy-light) 100%) !important;
224
- }
225
-
226
- .dataframe thead th {
227
- color: var(--gold) !important;
228
- font-weight: 600 !important;
229
- text-transform: uppercase !important;
230
- letter-spacing: 1.5px !important;
231
- padding: 20px !important;
232
- font-size: 0.85em !important;
233
- border: none !important;
234
- }
235
-
236
- .dataframe tbody td {
237
- padding: 25px 20px !important;
238
- color: var(--navy) !important;
239
- font-size: 0.95em !important;
240
- border-bottom: 1px solid rgba(10, 24, 40, 0.08) !important;
241
- transition: all 0.3s ease;
242
- }
243
-
244
- .dataframe tbody tr {
245
- transition: all 0.3s ease;
246
- }
247
-
248
- .dataframe tbody tr:hover {
249
- background: rgba(212, 175, 55, 0.08) !important;
250
- transform: scale(1.01);
251
- }
252
-
253
- /* Input Styling */
254
- .gr-box,
255
- input,
256
- textarea,
257
- select {
258
- background: rgba(248, 246, 240, 0.95) !important;
259
- border: 1px solid rgba(212, 175, 55, 0.3) !important;
260
- color: var(--navy) !important;
261
- border-radius: 8px !important;
262
- transition: all 0.3s ease !important;
263
- font-family: 'Montserrat', sans-serif !important;
264
- }
265
-
266
- .gr-box:focus,
267
- input:focus,
268
- textarea:focus,
269
- select:focus {
270
- border-color: var(--gold) !important;
271
- box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.15) !important;
272
- outline: none !important;
273
- }
274
-
275
- label {
276
- color: var(--cream) !important;
277
- font-weight: 500 !important;
278
- text-transform: uppercase !important;
279
- letter-spacing: 1px !important;
280
- font-size: 0.85em !important;
281
- margin-bottom: 8px !important;
282
- }
283
-
284
- /* Radio Buttons */
285
- .gr-radio {
286
- background: transparent !important;
287
- }
288
-
289
- .gr-radio label {
290
- color: var(--cream) !important;
291
- padding: 12px 20px !important;
292
- border: 1px solid rgba(212, 175, 55, 0.3) !important;
293
- border-radius: 8px !important;
294
- margin: 5px !important;
295
- transition: all 0.3s ease !important;
296
- cursor: pointer !important;
297
- text-transform: none !important;
298
- letter-spacing: 0.5px !important;
299
- }
300
-
301
- .gr-radio label:hover {
302
- background: rgba(212, 175, 55, 0.1) !important;
303
- border-color: var(--gold) !important;
304
- }
305
-
306
- .gr-radio input:checked + label {
307
- background: var(--gold) !important;
308
- color: var(--navy) !important;
309
- border-color: var(--gold) !important;
310
- font-weight: 600 !important;
311
- }
312
-
313
- /* Buttons */
314
- .gr-button {
315
- background: linear-gradient(135deg, var(--gold-dark) 0%, var(--gold) 100%) !important;
316
- color: var(--navy) !important;
317
- border: none !important;
318
- padding: 15px 35px !important;
319
- font-weight: 600 !important;
320
- text-transform: uppercase !important;
321
- letter-spacing: 2px !important;
322
- border-radius: 8px !important;
323
- transition: all 0.4s ease !important;
324
- box-shadow: 0 5px 20px rgba(212, 175, 55, 0.3) !important;
325
- font-size: 0.9em !important;
326
- position: relative !important;
327
- overflow: hidden !important;
328
- }
329
-
330
- .gr-button::before {
331
- content: '';
332
- position: absolute;
333
- top: 50%;
334
- left: 50%;
335
- width: 0;
336
- height: 0;
337
- border-radius: 50%;
338
- background: rgba(255, 255, 255, 0.3);
339
- transform: translate(-50%, -50%);
340
- transition: width 0.6s, height 0.6s;
341
- }
342
-
343
- .gr-button:hover::before {
344
- width: 300px;
345
- height: 300px;
346
- }
347
-
348
- .gr-button:hover {
349
- transform: translateY(-2px) !important;
350
- box-shadow: 0 8px 30px rgba(212, 175, 55, 0.5) !important;
351
- }
352
-
353
- .gr-button-secondary {
354
- background: transparent !important;
355
- border: 2px solid var(--gold) !important;
356
- color: var(--gold) !important;
357
- }
358
-
359
- .gr-button-secondary:hover {
360
- background: var(--gold) !important;
361
- color: var(--navy) !important;
362
- }
363
-
364
- /* Info Box */
365
- .info-box {
366
- background: rgba(212, 175, 55, 0.1);
367
- border-left: 4px solid var(--gold);
368
- padding: 20px 25px;
369
- border-radius: 0 8px 8px 0;
370
- color: var(--cream);
371
- margin: 20px 0;
372
- font-size: 0.95em;
373
- line-height: 1.6;
374
- backdrop-filter: blur(5px);
375
- }
376
-
377
- .info-box strong {
378
- color: var(--gold);
379
- font-weight: 600;
380
- }
381
-
382
- /* Dashboard Cards */
383
- .dashboard-section {
384
- background: rgba(248, 246, 240, 0.05);
385
- border: 1px solid rgba(212, 175, 55, 0.2);
386
- border-radius: 15px;
387
- padding: 30px;
388
- margin: 20px 0;
389
- backdrop-filter: blur(10px);
390
- }
391
-
392
- .dashboard-title {
393
- font-family: 'Cormorant Garamond', serif;
394
- font-size: 1.8em;
395
- color: var(--gold);
396
- margin-bottom: 10px;
397
- font-weight: 600;
398
- letter-spacing: 1px;
399
- }
400
-
401
- .dashboard-subtitle {
402
- color: var(--cream);
403
- opacity: 0.8;
404
- font-size: 0.9em;
405
- letter-spacing: 1px;
406
- text-transform: uppercase;
407
- margin-bottom: 25px;
408
- }
409
-
410
- /* Footer */
411
- .luxury-footer {
412
- text-align: center;
413
- margin-top: 80px;
414
- padding: 40px 20px;
415
- border-top: 1px solid rgba(212, 175, 55, 0.2);
416
- color: var(--cream);
417
- opacity: 0.6;
418
- font-size: 0.85em;
419
- letter-spacing: 1.5px;
420
- text-transform: uppercase;
421
- }
422
-
423
- /* Status Badge */
424
- .status-available {
425
- color: #2ECC71;
426
- font-weight: 600;
427
- }
428
-
429
- .status-occupied {
430
- color: #E74C3C;
431
- font-weight: 600;
432
- }
433
-
434
- /* Markdown Styling */
435
- .markdown-text {
436
- color: var(--cream) !important;
437
- line-height: 1.8 !important;
438
- }
439
-
440
- .markdown-text h3 {
441
- font-family: 'Cormorant Garamond', serif !important;
442
- color: var(--gold) !important;
443
- font-size: 1.6em !important;
444
- margin-bottom: 15px !important;
445
- font-weight: 600 !important;
446
- }
447
-
448
- .markdown-text p {
449
- color: var(--cream) !important;
450
- opacity: 0.9 !important;
451
- }
452
-
453
- /* Success Message */
454
- .success-message {
455
- background: linear-gradient(135deg, rgba(46, 204, 113, 0.2) 0%, rgba(46, 204, 113, 0.1) 100%);
456
- border: 1px solid rgba(46, 204, 113, 0.5);
457
- color: #2ECC71;
458
- padding: 20px;
459
- border-radius: 8px;
460
- text-align: center;
461
- font-weight: 600;
462
- letter-spacing: 1px;
463
- animation: slideInFromRight 0.5s ease-out;
464
- }
465
-
466
- @keyframes slideInFromRight {
467
- from {
468
- opacity: 0;
469
- transform: translateX(30px);
470
- }
471
- to {
472
- opacity: 1;
473
- transform: translateX(0);
474
- }
475
- }
476
-
477
- /* Responsive Design */
478
- @media (max-width: 768px) {
479
- .luxury-header h1 {
480
- font-size: 2.5em !important;
481
- }
482
-
483
- .promo-banner .promo-message {
484
- font-size: 1.4em;
485
- }
486
-
487
- .tabs button {
488
- padding: 12px 20px !important;
489
- font-size: 0.85em !important;
490
- }
491
- }
492
-
493
- /* Scrollbar Styling */
494
- ::-webkit-scrollbar {
495
- width: 10px;
496
- }
497
-
498
- ::-webkit-scrollbar-track {
499
- background: var(--navy);
500
- }
501
-
502
- ::-webkit-scrollbar-thumb {
503
- background: var(--gold);
504
- border-radius: 5px;
505
- }
506
-
507
- ::-webkit-scrollbar-thumb:hover {
508
- background: var(--gold-dark);
509
- }
510
- """
511
-
512
- # --- ENHANCED DATA ---
513
- data = {
514
- "🏨 Hotel Name": [
515
- "Coral Reef View Resort",
516
- "Male' Grand Stay Hotel",
517
- "Villi Blue Inn",
518
- "Azure Lagoon Suites",
519
- "Paradise Isle Boutique"
520
- ],
521
- "📍 Location": [
522
- "Hulhumale Phase 1",
523
- "Male' City Center",
524
- "Villingili Island",
525
- "Hulhumale Phase 2",
526
- "Male' Waterfront"
527
- ],
528
- "💰 Price/Night": [
529
- "850 MVR",
530
- "1,100 MVR",
531
- "650 MVR",
532
- "950 MVR",
533
- "1,250 MVR"
534
- ],
535
- "⭐ Rating": [
536
- "4.8/5.0",
537
- "4.6/5.0",
538
- "4.5/5.0",
539
- "4.9/5.0",
540
- "4.7/5.0"
541
- ],
542
- "✅ Status": [
543
- "Available",
544
- "Available",
545
- "Occupied",
546
- "Available",
547
- "Available"
548
- ],
549
- "📞 Direct Contact": [
550
- "+960 777-1234",
551
- "+960 778-5678",
552
- "+960 779-9101",
553
- "+960 780-2345",
554
- "+960 781-6789"
555
- ]
556
- }
557
- df = pd.DataFrame(data)
558
-
559
- # --- FUNCTIONS ---
560
- def filter_hotels(location):
561
- if location == "All":
562
- return df
563
- elif location == "Male' City":
564
- filtered = df[df["📍 Location"].str.contains("Male'", case=False, na=False)]
565
- elif location == "Hulhumale":
566
- filtered = df[df["📍 Location"].str.contains("Hulhumale", case=False, na=False)]
567
- elif location == "Villingili":
568
- filtered = df[df["📍 Location"].str.contains("Villingili", case=False, na=False)]
569
- else:
570
- filtered = df
571
- return filtered
572
-
573
- def refresh_data():
574
- return df, "✨ Availability refreshed successfully!"
575
-
576
- def send_promotion(hotel, message):
577
- if not hotel or not message:
578
- return gr.update(visible=False), "⚠️ Please select a hotel and enter a promotion message."
579
-
580
- time_now = datetime.now().strftime("%I:%M %p")
581
- date_now = datetime.now().strftime("%B %d, %Y")
582
-
583
- promo_html = f"""
584
- <div class='promo-banner'>
585
- <div class='promo-title'>⚡ Exclusive Flash Offer</div>
586
- <div class='promo-message'>{message}</div>
587
- <div style='margin: 15px 0; font-size: 1.1em; font-weight: 600;'>📍 {hotel}</div>
588
- <div class='promo-time'>Posted {time_now} • {date_now} • Valid Tonight Only</div>
589
- </div>
590
- """
591
-
592
- return gr.update(value=promo_html, visible=True), f"<div class='success-message'>✅ Promotion successfully broadcast to all users!</div>"
593
-
594
- def update_listing(hotel, status, price):
595
- if not hotel:
596
- return "⚠️ Please select your property first."
597
-
598
- success_msg = f"""
599
- <div class='success-message'>
600
- ✅ <strong>{hotel}</strong> updated successfully!<br>
601
- Status: {status} | Price: {price} MVR/night
602
- </div>
603
- """
604
- return success_msg
605
-
606
- # --- UI LAYOUT ---
607
- with gr.Blocks(css=custom_css, title="Hulhumale Luxury Hotel Direct", theme=gr.themes.Base()) as demo:
608
-
609
- # Luxury Header
610
- gr.HTML("""
611
- <div class='luxury-header'>
612
- <h1>HULHUMALE DIRECT</h1>
613
- <div class='divider'></div>
614
- <div class='subtitle'>Luxury Accommodations • Direct Booking • Zero Commission</div>
615
- </div>
616
- """)
617
-
618
- # Promotion Display (Hidden by default)
619
- promo_display = gr.HTML(visible=False)
620
-
621
- with gr.Tabs() as tabs:
622
-
623
- # --- TRAVELER TAB ---
624
- with gr.Tab("🔍 Discover Your Stay"):
625
-
626
- gr.HTML("<div class='dashboard-section'>")
627
-
628
- with gr.Row():
629
- filter_location = gr.Radio(
630
- choices=["All", "Male' City", "Hulhumale", "Villingili"],
631
- value="All",
632
- label="Filter by Destination",
633
- elem_classes="location-filter"
634
- )
635
-
636
- room_table = gr.DataFrame(
637
- value=df,
638
- interactive=False,
639
- label="Available Properties",
640
- wrap=True
641
- )
642
-
643
- gr.HTML("""
644
- <div class='info-box'>
645
- <strong>🌟 How to Book:</strong> Contact hotels directly using the numbers listed above.
646
- Speak with owners personally, negotiate rates, and enjoy commission-free bookings.
647
- All properties are verified and family-operated.
648
- </div>
649
- """)
650
-
651
- with gr.Row():
652
- refresh_btn = gr.Button("🔄 Refresh Availability", variant="secondary", size="lg")
653
- refresh_status = gr.Markdown("")
654
-
655
- gr.HTML("</div>")
656
-
657
- # --- OWNER TAB ---
658
- with gr.Tab("⚙️ Property Management Portal"):
659
-
660
- gr.HTML("""
661
- <div class='dashboard-section'>
662
- <div class='dashboard-title'>Welcome to Your Dashboard</div>
663
- <div class='dashboard-subtitle'>Premium Client Portal • 350 MVR per Month</div>
664
- </div>
665
- """)
666
-
667
- with gr.Row():
668
- with gr.Column(scale=1):
669
- gr.HTML("<div class='dashboard-section'>")
670
- gr.Markdown("### 📊 Update Your Listing")
671
-
672
- owner_hotel = gr.Dropdown(
673
- choices=list(data["🏨 Hotel Name"]),
674
- label="Select Your Property",
675
- value=None
676
- )
677
-
678
- new_status = gr.Radio(
679
- choices=["Available", "Occupied"],
680
- label="Current Availability Status",
681
- value="Available"
682
- )
683
-
684
- update_price = gr.Textbox(
685
- label="Tonight's Rate (MVR)",
686
- placeholder="e.g., 850",
687
- value=""
688
- )
689
-
690
- update_btn = gr.Button("💾 Update Live Listing", variant="primary", size="lg")
691
- update_status = gr.HTML("")
692
-
693
- gr.HTML("</div>")
694
-
695
- with gr.Column(scale=1):
696
- gr.HTML("<div class='dashboard-section'>")
697
- gr.Markdown("### 📣 Marketing & Promotions")
698
-
699
- gr.HTML("""
700
- <div class='info-box'>
701
- Broadcast instant promotions to all active users browsing the platform.
702
- Perfect for last-minute deals, early bird specials, or flash sales.
703
- </div>
704
- """)
705
-
706
- promo_hotel = gr.Dropdown(
707
- choices=list(data["🏨 Hotel Name"]),
708
- label="Property Name",
709
- value=None
710
- )
711
-
712
- promo_text = gr.Textbox(
713
- label="Promotion Message",
714
- placeholder="e.g., 20% OFF for airport arrivals before midnight!",
715
- lines=3
716
- )
717
-
718
- promo_btn = gr.Button("🚀 Broadcast Promotion", variant="primary", size="lg")
719
- promo_status = gr.HTML("")
720
-
721
- gr.HTML("</div>")
722
-
723
- # --- EVENT HANDLERS ---
724
- filter_location.change(
725
- fn=filter_hotels,
726
- inputs=[filter_location],
727
- outputs=[room_table]
728
- )
729
-
730
- refresh_btn.click(
731
- fn=refresh_data,
732
- inputs=[],
733
- outputs=[room_table, refresh_status]
734
- )
735
-
736
- update_btn.click(
737
- fn=update_listing,
738
- inputs=[owner_hotel, new_status, update_price],
739
- outputs=[update_status]
740
- )
741
-
742
- promo_btn.click(
743
- fn=send_promotion,
744
- inputs=[promo_hotel, promo_text],
745
- outputs=[promo_display, promo_status]
746
- )
747
-
748
- # Luxury Footer
749
- gr.HTML("""
750
- <div class='luxury-footer'>
751
- <div style='margin-bottom: 10px;'>✦ ✦ ✦</div>
752
- Hulhumale Direct • Connecting Travelers With Local Hospitality Since 2024
753
- <div style='margin-top: 10px; font-size: 0.75em; opacity: 0.5;'>
754
- A Premium Local Marketplace
755
- </div>
756
- </div>
757
- """)
758
-
759
- # Launch the app
760
- if __name__ == "__main__":
761
- demo.launch(share=False)