user commited on
Commit
f4a24ca
·
1 Parent(s): 23b5ac6

Replace with Next.js + shadcn frontend, no Gradio

Browse files
Dockerfile CHANGED
@@ -1,8 +1,18 @@
1
- FROM python:3.11-slim
2
  WORKDIR /app
3
- COPY backend/google_play_scraper/ ./google_play_scraper/
4
- COPY app.py .
5
- COPY backend/google_play_scraper/requirements.txt ./requirements.txt
6
- RUN pip install --no-cache-dir -r requirements.txt
7
- EXPOSE 7860
8
- CMD ["python", "app.py"]
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:18-alpine AS builder
2
  WORKDIR /app
3
+ COPY frontend/package.json ./
4
+ RUN npm install
5
+ COPY frontend/ ./
6
+ RUN npm run build
7
+
8
+ FROM node:18-alpine AS runner
9
+ WORKDIR /app
10
+ ENV NODE_ENV=production
11
+
12
+ COPY --from=builder /app/.next/standalone ./
13
+ COPY --from=builder /app/.next/static ./.next/static
14
+ COPY --from=builder /app/public ./public
15
+
16
+ EXPOSE 3000
17
+ ENV PORT=3000
18
+ CMD ["node", "server.js"]
README.md CHANGED
@@ -3,17 +3,17 @@ title: PlayStore Scraper
3
  emoji: 🔍
4
  colorFrom: blue
5
  colorTo: green
6
- sdk: gradio
7
- app_file: app.py
8
  pinned: false
9
  private: true
10
  ---
11
 
12
  # PlayStore Scraper
13
 
14
- Search Google Play Store applications using the backend scraper.
15
 
16
  ## Structure
17
 
18
- - **backend/google_play_scraper/** - Python scraper library
19
- - **frontend/** - Next.js UI (coming soon)
 
3
  emoji: 🔍
4
  colorFrom: blue
5
  colorTo: green
6
+ sdk: docker
7
+ app_file: Dockerfile
8
  pinned: false
9
  private: true
10
  ---
11
 
12
  # PlayStore Scraper
13
 
14
+ Search Google Play Store applications with a clean, modern UI.
15
 
16
  ## Structure
17
 
18
+ - **backend/google_play_scraper/** - Python scraper library (empty, to be implemented)
19
+ - **frontend/** - Next.js + shadcn/ui frontend
app.py DELETED
@@ -1,18 +0,0 @@
1
- import gradio as gr
2
- import sys
3
- import os
4
-
5
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), "backend"))
6
-
7
- def search_apps(query):
8
- return f"Searching for: {query}"
9
-
10
- demo = gr.Interface(
11
- fn=search_apps,
12
- inputs=gr.Textbox(label="App Name", placeholder="Enter app name..."),
13
- outputs=gr.Textbox(label="Results"),
14
- title="PlayStore Scraper",
15
- description="Search Google Play Store applications"
16
- )
17
-
18
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/app/layout.tsx CHANGED
@@ -6,7 +6,7 @@ const inter = Inter({ subsets: ["latin"] });
6
 
7
  export const metadata: Metadata = {
8
  title: "PlayStore Scraper",
9
- description: "Search Google Play Store apps",
10
  };
11
 
12
  export default function RootLayout({
@@ -15,7 +15,7 @@ export default function RootLayout({
15
  children: React.ReactNode;
16
  }>) {
17
  return (
18
- <html lang="en">
19
  <body className={inter.className}>{children}</body>
20
  </html>
21
  );
 
6
 
7
  export const metadata: Metadata = {
8
  title: "PlayStore Scraper",
9
+ description: "Search Google Play Store applications",
10
  };
11
 
12
  export default function RootLayout({
 
15
  children: React.ReactNode;
16
  }>) {
17
  return (
18
+ <html lang="en" className="dark">
19
  <body className={inter.className}>{children}</body>
20
  </html>
21
  );
frontend/app/page.tsx CHANGED
@@ -1,57 +1,98 @@
1
  "use client";
2
 
3
  import { useState } from "react";
4
- import { Search } from "lucide-react";
5
  import { Button } from "@/components/ui/button";
6
  import { Input } from "@/components/ui/input";
7
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
8
 
9
  export default function Home() {
10
  const [searchQuery, setSearchQuery] = useState("");
11
  const [isSearching, setIsSearching] = useState(false);
 
 
12
 
13
  const handleSearch = async () => {
14
  if (!searchQuery.trim()) return;
15
  setIsSearching(true);
16
- console.log("Searching for:", searchQuery);
17
- setTimeout(() => setIsSearching(false), 1000);
 
 
 
 
 
 
18
  };
19
 
20
  return (
21
- <main className="min-h-screen bg-gradient-to-b from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800">
22
- <div className="container mx-auto py-16 px-4">
23
  <div className="text-center mb-12">
24
- <h1 className="text-4xl font-bold text-slate-900 dark:text-slate-50 mb-4">
25
  PlayStore Scraper
26
  </h1>
27
- <p className="text-slate-600 dark:text-slate-400">
28
  Search and explore Google Play Store applications
29
  </p>
30
  </div>
31
 
32
- <Card className="max-w-2xl mx-auto">
33
- <CardHeader>
34
- <CardTitle>Search Apps</CardTitle>
35
- <CardDescription>
36
- Enter an app name to search for available apps
37
- </CardDescription>
38
- </CardHeader>
39
- <CardContent>
40
- <div className="flex gap-2">
41
- <Input
42
- placeholder="Enter app name..."
43
- value={searchQuery}
44
- onChange={(e) => setSearchQuery(e.target.value)}
45
- onKeyDown={(e) => e.key === "Enter" && handleSearch()}
46
- className="flex-1"
47
- />
48
- <Button onClick={handleSearch} disabled={isSearching}>
49
- <Search className="w-4 h-4 mr-2" />
50
- {isSearching ? "Searching..." : "Search"}
51
- </Button>
52
- </div>
53
- </CardContent>
54
- </Card>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  </div>
56
  </main>
57
  );
 
1
  "use client";
2
 
3
  import { useState } from "react";
4
+ import { Search, Loader2 } from "lucide-react";
5
  import { Button } from "@/components/ui/button";
6
  import { Input } from "@/components/ui/input";
 
7
 
8
  export default function Home() {
9
  const [searchQuery, setSearchQuery] = useState("");
10
  const [isSearching, setIsSearching] = useState(false);
11
+ const [results, setResults] = useState<any[]>([]);
12
+ const [hasSearched, setHasSearched] = useState(false);
13
 
14
  const handleSearch = async () => {
15
  if (!searchQuery.trim()) return;
16
  setIsSearching(true);
17
+ setHasSearched(true);
18
+
19
+ setTimeout(() => {
20
+ setResults([
21
+ { title: searchQuery, appId: "com.example.app", rating: 4.5, installs: "1,000,000+" },
22
+ ]);
23
+ setIsSearching(false);
24
+ }, 1000);
25
  };
26
 
27
  return (
28
+ <main className="min-h-screen bg-background text-foreground">
29
+ <div className="container mx-auto py-16 px-4 max-w-4xl">
30
  <div className="text-center mb-12">
31
+ <h1 className="text-4xl font-bold tracking-tight mb-4">
32
  PlayStore Scraper
33
  </h1>
34
+ <p className="text-muted-foreground text-lg">
35
  Search and explore Google Play Store applications
36
  </p>
37
  </div>
38
 
39
+ <div className="flex gap-3 mb-8">
40
+ <Input
41
+ placeholder="Enter app name or package ID..."
42
+ value={searchQuery}
43
+ onChange={(e) => setSearchQuery(e.target.value)}
44
+ onKeyDown={(e) => e.key === "Enter" && handleSearch()}
45
+ className="h-12 text-lg"
46
+ />
47
+ <Button
48
+ onClick={handleSearch}
49
+ disabled={isSearching}
50
+ size="lg"
51
+ className="h-12 px-8"
52
+ >
53
+ {isSearching ? (
54
+ <Loader2 className="h-5 w-5 animate-spin" />
55
+ ) : (
56
+ <Search className="h-5 w-5 mr-2" />
57
+ )}
58
+ Search
59
+ </Button>
60
+ </div>
61
+
62
+ {hasSearched && !isSearching && results.length === 0 && (
63
+ <div className="text-center text-muted-foreground py-12">
64
+ No results found. Try a different search term.
65
+ </div>
66
+ )}
67
+
68
+ {results.length > 0 && (
69
+ <div className="space-y-4">
70
+ {results.map((app, index) => (
71
+ <div
72
+ key={index}
73
+ className="p-4 rounded-lg border bg-card text-card-foreground shadow-sm hover:shadow-md transition-shadow cursor-pointer"
74
+ >
75
+ <div className="flex items-center justify-between">
76
+ <div>
77
+ <h3 className="font-semibold text-lg">{app.title}</h3>
78
+ <p className="text-sm text-muted-foreground">{app.appId}</p>
79
+ </div>
80
+ <div className="text-right">
81
+ <div className="flex items-center gap-1">
82
+ <span className="text-yellow-500">★</span>
83
+ <span className="font-medium">{app.rating}</span>
84
+ </div>
85
+ <p className="text-sm text-muted-foreground">{app.installs}</p>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ ))}
90
+ </div>
91
+ )}
92
+
93
+ <div className="mt-16 text-center text-sm text-muted-foreground">
94
+ <p>Enter an app name to search • Example: "Facebook" or "com.facebook.katana"</p>
95
+ </div>
96
  </div>
97
  </main>
98
  );
frontend/components/ui/button.tsx CHANGED
@@ -18,7 +18,7 @@ const buttonVariants = cva(
18
  size: {
19
  default: "h-10 px-4 py-2",
20
  sm: "h-9 rounded-md px-3",
21
- lg: "h-11 rounded-md px-8",
22
  icon: "h-10 w-10",
23
  },
24
  },
 
18
  size: {
19
  default: "h-10 px-4 py-2",
20
  sm: "h-9 rounded-md px-3",
21
+ lg: "h-11 rounded-md px-8 text-base",
22
  icon: "h-10 w-10",
23
  },
24
  },
frontend/next.config.mjs CHANGED
@@ -1,4 +1,6 @@
1
  /** @type {import('next').NextConfig} */
2
- const nextConfig = {};
 
 
3
 
4
  export default nextConfig;
 
1
  /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ output: 'standalone',
4
+ };
5
 
6
  export default nextConfig;
frontend/package.json CHANGED
@@ -27,4 +27,4 @@
27
  "postcss": "^8",
28
  "tailwindcss": "^3.4.1"
29
  }
30
- }
 
27
  "postcss": "^8",
28
  "tailwindcss": "^3.4.1"
29
  }
30
+ }