Skip to content

Commit

Permalink
UI done
Browse files Browse the repository at this point in the history
  • Loading branch information
IqbalLx committed Jan 5, 2025
1 parent d1f3b57 commit 8992f40
Show file tree
Hide file tree
Showing 92 changed files with 781 additions and 2,129 deletions.
51 changes: 3 additions & 48 deletions chrome-extension/manifest.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,8 @@
import fs from 'node:fs';
import deepmerge from 'deepmerge';

const packageJson = JSON.parse(fs.readFileSync('../package.json', 'utf8'));

const isFirefox = process.env.__FIREFOX__ === 'true';

/**
* If you want to disable the sidePanel, you can delete withSidePanel function and remove the sidePanel HoC on the manifest declaration.
*
* ```js
* const manifest = { // remove `withSidePanel()`
* ```
*/
function withSidePanel(manifest) {
// Firefox does not support sidePanel
if (isFirefox) {
return manifest;
}
return deepmerge(manifest, {
side_panel: {
default_path: 'side-panel/index.html',
},
permissions: ['sidePanel'],
});
}

/**
* After changing, please reload the extension at `chrome://extensions`
* @type {chrome.runtime.ManifestV3}
*/
const manifest = withSidePanel({
const manifest = {
manifest_version: 3,
default_locale: 'en',
/**
Expand All @@ -40,7 +13,7 @@ const manifest = withSidePanel({
version: packageJson.version,
description: '__MSG_extensionDescription__',
host_permissions: ['<all_urls>'],
permissions: ['storage', 'scripting', 'tabs', 'notifications'],
permissions: ['tabs'],
options_page: 'options/index.html',
background: {
service_worker: 'background.iife.js',
Expand All @@ -50,33 +23,15 @@ const manifest = withSidePanel({
default_popup: 'popup/index.html',
default_icon: 'icon-34.png',
},
chrome_url_overrides: {
newtab: 'new-tab/index.html',
},
icons: {
128: 'icon-128.png',
},
content_scripts: [
{
matches: ['http://*/*', 'https://*/*', '<all_urls>'],
js: ['content/index.iife.js'],
},
{
matches: ['http://*/*', 'https://*/*', '<all_urls>'],
js: ['content-ui/index.iife.js'],
},
{
matches: ['http://*/*', 'https://*/*', '<all_urls>'],
css: ['content.css'], // public folder
},
],
devtools_page: 'devtools/index.html',
web_accessible_resources: [
{
resources: ['*.js', '*.css', '*.svg', 'icon-128.png', 'icon-34.png'],
matches: ['*://*/*'],
},
],
});
};

export default manifest;
8 changes: 0 additions & 8 deletions chrome-extension/src/background/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
import 'webextension-polyfill';
import { exampleThemeStorage } from '@extension/storage';

exampleThemeStorage.get().then(theme => {
console.log('theme', theme);
});

console.log('background loaded');
console.log("Edit 'chrome-extension/src/background/index.ts' and save to reload.");
17 changes: 10 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "chrome-extension-boilerplate-react-vite",
"version": "0.3.5",
"description": "chrome extension boilerplate",
"name": "honest",
"version": "0.1.0",
"description": "Not only sweet as Honey, but Honest. Find and share deals with communities",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite.git"
"url": "https://github.com/IqbalLx/honest"
},
"type": "module",
"scripts": {
Expand Down Expand Up @@ -57,12 +57,12 @@
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"rimraf": "^6.0.1",
"run-script-os": "^1.1.6",
"tailwindcss": "^3.4.14",
"tslib": "^2.6.3",
"typescript": "5.5.4",
"turbo": "^2.3.3",
"vite": "6.0.5",
"run-script-os": "^1.1.6"
"typescript": "5.5.4",
"vite": "6.0.5"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json}": [
Expand All @@ -72,5 +72,8 @@
"packageManager": "pnpm@9.15.1",
"engines": {
"node": ">=22.12.0"
},
"volta": {
"node": "22.12.0"
}
}
5 changes: 1 addition & 4 deletions packages/i18n/lib/getMessageFromLocale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,18 @@
* Do not edit this file directly
*/
import enMessage from '../locales/en/messages.json';
import koMessage from '../locales/ko/messages.json';

export function getMessageFromLocale(locale: string) {
switch (locale) {
case 'en':
return enMessage;
case 'ko':
return koMessage;
default:
throw new Error('Unsupported locale');
}
}

export const defaultLocale = (() => {
const locales = ['en', 'ko'];
const locales = ['en'];
const firstLocale = locales[0];
const defaultLocale = Intl.DateTimeFormat().resolvedOptions().locale.replace('-', '_');
if (locales.includes(defaultLocale)) {
Expand Down
5 changes: 2 additions & 3 deletions packages/i18n/lib/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
* Do not edit this file directly
*/
import type enMessage from '../locales/en/messages.json';
import type koMessage from '../locales/ko/messages.json';

export type MessageKey = keyof typeof enMessage & keyof typeof koMessage;
export type MessageKey = keyof typeof enMessage;

export type DevLocale = 'en' | 'ko';
export type DevLocale = 'en';
4 changes: 2 additions & 2 deletions packages/i18n/locales/en/messages.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"extensionDescription": {
"description": "Extension description",
"message": "Chrome extension boilerplate developed with Vite, React and Typescript"
"message": "Not only sweet as Honey, but Honest. Find and share deals with communities"
},
"extensionName": {
"description": "Extension name",
"message": "Chrome extension boilerplate"
"message": "Honest"
},
"toggleTheme": {
"message": "Toggle theme"
Expand Down
30 changes: 0 additions & 30 deletions packages/i18n/locales/ko/messages.json

This file was deleted.

20 changes: 20 additions & 0 deletions packages/ui/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/lib/components",
"utils": "@/lib/utils",
"ui": "@/lib/components/ui",
"lib": "@/lib"
},
"iconLibrary": "lucide"
}
20 changes: 0 additions & 20 deletions packages/ui/lib/components/Button.tsx

This file was deleted.

49 changes: 49 additions & 0 deletions packages/ui/lib/components/CouponList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useState } from 'react';
import { Button } from './ui/button';
import { Card, CardContent, CardFooter } from './ui/card';
import { Clipboard, CheckCircle } from 'lucide-react';

import FeedbackButton from './FeedbackButton';

interface Coupon {
id: string;
code: string;
description: string;
}

interface CouponListProps {
coupons: Coupon[];
}

export default function CouponList({ coupons }: CouponListProps) {
const [copiedCoupon, setCopiedCoupon] = useState<string | null>(null);

const handleCopy = (code: string) => {
navigator.clipboard.writeText(code);
setCopiedCoupon(code);
setTimeout(() => setCopiedCoupon(null), 2000); // Reset after 2 seconds
};

return (
<div className="space-y-4">
{coupons.map(coupon => (
<Card key={coupon.id}>
<CardContent className="pt-4">
<h2 className="text-lg font-semibold">{coupon.code}</h2>
<p className="text-sm text-gray-500">{coupon.description}</p>
</CardContent>
<CardFooter className="flex justify-between">
<Button variant="outline" size="icon" onClick={() => handleCopy(coupon.code)} className="w-10 h-10">
{copiedCoupon === coupon.code ? (
<CheckCircle className="h-4 w-4 text-green-500" />
) : (
<Clipboard className="h-4 w-4" />
)}
</Button>
<FeedbackButton couponId={coupon.id} />
</CardFooter>
</Card>
))}
</div>
);
}
13 changes: 13 additions & 0 deletions packages/ui/lib/components/EmptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { FileX } from 'lucide-react';

export default function EmptyState() {
return (
<div className="flex flex-col items-center justify-center h-full text-center p-4">
<FileX className="w-16 h-16 text-gray-400 mb-4" />
<h2 className="text-xl font-semibold mb-2">No Coupons Available</h2>
<p className="text-gray-500">
There are currently no coupons available for this site. Check back later or add a new coupon if you know one!
</p>
</div>
);
}
46 changes: 46 additions & 0 deletions packages/ui/lib/components/FeedbackButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useState } from 'react';
import { Button } from './ui/button';
import { ThumbsUp, ThumbsDown } from 'lucide-react';

import { cn } from '../utils';

interface FeedbackButtonProps {
couponId: string;
}

export default function FeedbackButton({ couponId }: FeedbackButtonProps) {
const [feedback, setFeedback] = useState<'positive' | 'negative' | null>(null);

const handleFeedback = (type: 'positive' | 'negative') => {
setFeedback(type);
// In a real app, you would send this feedback to your backend
console.log(`Feedback for coupon ${couponId}: ${type}`);
};

return (
<div className="flex space-x-2">
<Button
variant="outline"
size="icon"
onClick={() => handleFeedback('positive')}
disabled={feedback !== null}
className={cn(
'transition-colors',
feedback === 'positive' && 'bg-green-500 text-white border-green-500 hover:bg-green-500 hover:text-white',
)}>
<ThumbsUp className="h-4 w-4" />
</Button>
<Button
variant="outline"
size="icon"
onClick={() => handleFeedback('negative')}
disabled={feedback !== null}
className={cn(
'transition-colors',
feedback === 'negative' && 'bg-red-500 text-white border-red-500 hover:bg-red-500 hover:text-white',
)}>
<ThumbsDown className="h-4 w-4" />
</Button>
</div>
);
}
32 changes: 32 additions & 0 deletions packages/ui/lib/components/NewCouponForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useState } from 'react';
import { Button } from './ui/button';
import { Input } from './ui/input';
import { Textarea } from './ui/textarea';

interface NewCouponFormProps {
onSubmit: (coupon: { code: string; description: string }) => void;
onCancel: () => void;
}

export default function NewCouponForm({ onSubmit, onCancel }: NewCouponFormProps) {
const [code, setCode] = useState('');
const [description, setDescription] = useState('');

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSubmit({ code, description });
};

return (
<form onSubmit={handleSubmit} className="space-y-4">
<Input placeholder="Coupon Code" value={code} onChange={e => setCode(e.target.value)} required />
<Textarea placeholder="Description" value={description} onChange={e => setDescription(e.target.value)} required />
<div className="flex justify-between">
<Button type="submit">Submit</Button>
<Button variant="outline" onClick={onCancel}>
Cancel
</Button>
</div>
</form>
);
}
Loading

0 comments on commit 8992f40

Please sign in to comment.