diff --git a/chrome-extension/manifest.js b/chrome-extension/manifest.js index d88070c..4db6da5 100755 --- a/chrome-extension/manifest.js +++ b/chrome-extension/manifest.js @@ -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', /** @@ -40,7 +13,7 @@ const manifest = withSidePanel({ version: packageJson.version, description: '__MSG_extensionDescription__', host_permissions: [''], - permissions: ['storage', 'scripting', 'tabs', 'notifications'], + permissions: ['tabs'], options_page: 'options/index.html', background: { service_worker: 'background.iife.js', @@ -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://*/*', ''], - js: ['content/index.iife.js'], - }, - { - matches: ['http://*/*', 'https://*/*', ''], - js: ['content-ui/index.iife.js'], - }, - { - matches: ['http://*/*', 'https://*/*', ''], - 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; diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts index b16541b..d21a986 100644 --- a/chrome-extension/src/background/index.ts +++ b/chrome-extension/src/background/index.ts @@ -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."); diff --git a/package.json b/package.json index 46581e8..d3222d3 100644 --- a/package.json +++ b/package.json @@ -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": { @@ -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}": [ @@ -72,5 +72,8 @@ "packageManager": "pnpm@9.15.1", "engines": { "node": ">=22.12.0" + }, + "volta": { + "node": "22.12.0" } } diff --git a/packages/i18n/lib/getMessageFromLocale.ts b/packages/i18n/lib/getMessageFromLocale.ts index e27539e..89d4660 100644 --- a/packages/i18n/lib/getMessageFromLocale.ts +++ b/packages/i18n/lib/getMessageFromLocale.ts @@ -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)) { diff --git a/packages/i18n/lib/type.ts b/packages/i18n/lib/type.ts index 6f038c1..462afda 100644 --- a/packages/i18n/lib/type.ts +++ b/packages/i18n/lib/type.ts @@ -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'; diff --git a/packages/i18n/locales/en/messages.json b/packages/i18n/locales/en/messages.json index 5a18652..38ce5ea 100644 --- a/packages/i18n/locales/en/messages.json +++ b/packages/i18n/locales/en/messages.json @@ -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" diff --git a/packages/i18n/locales/ko/messages.json b/packages/i18n/locales/ko/messages.json deleted file mode 100644 index fef8a7e..0000000 --- a/packages/i18n/locales/ko/messages.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "extensionDescription": { - "description": "Extension description", - "message": "React, Typescript, Vite를 사용한 크롬 익스텐션 보일러플레이트입니다." - }, - "extensionName": { - "description": "Extension name", - "message": "크롬 익스텐션 보일러플레이트" - }, - "toggleTheme": { - "message": "테마 변경" - }, - "loading": { - "message": "로딩 중..." - }, - "greeting": { - "description": "인사 메시지", - "message": "안녕하세요, 제 이름은 $NAME$입니다.", - "placeholders": { - "name": { - "content": "$1", - "example": "서종학" - } - } - }, - "hello": { - "description": "Placeholder 예시", - "message": "안녕 $1" - } -} diff --git a/packages/ui/components.json b/packages/ui/components.json new file mode 100644 index 0000000..a391f28 --- /dev/null +++ b/packages/ui/components.json @@ -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" +} diff --git a/packages/ui/lib/components/Button.tsx b/packages/ui/lib/components/Button.tsx deleted file mode 100644 index 88c4100..0000000 --- a/packages/ui/lib/components/Button.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import type { ComponentPropsWithoutRef } from 'react'; -import { cn } from '../utils'; - -export type ButtonProps = { - theme?: 'light' | 'dark'; -} & ComponentPropsWithoutRef<'button'>; - -export function Button({ theme, className, children, ...props }: ButtonProps) { - return ( - - ); -} diff --git a/packages/ui/lib/components/CouponList.tsx b/packages/ui/lib/components/CouponList.tsx new file mode 100644 index 0000000..3dbdce3 --- /dev/null +++ b/packages/ui/lib/components/CouponList.tsx @@ -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(null); + + const handleCopy = (code: string) => { + navigator.clipboard.writeText(code); + setCopiedCoupon(code); + setTimeout(() => setCopiedCoupon(null), 2000); // Reset after 2 seconds + }; + + return ( +
+ {coupons.map(coupon => ( + + +

{coupon.code}

+

{coupon.description}

+
+ + + + +
+ ))} +
+ ); +} diff --git a/packages/ui/lib/components/EmptyState.tsx b/packages/ui/lib/components/EmptyState.tsx new file mode 100644 index 0000000..5496988 --- /dev/null +++ b/packages/ui/lib/components/EmptyState.tsx @@ -0,0 +1,13 @@ +import { FileX } from 'lucide-react'; + +export default function EmptyState() { + return ( +
+ +

No Coupons Available

+

+ There are currently no coupons available for this site. Check back later or add a new coupon if you know one! +

+
+ ); +} diff --git a/packages/ui/lib/components/FeedbackButton.tsx b/packages/ui/lib/components/FeedbackButton.tsx new file mode 100644 index 0000000..5b369ea --- /dev/null +++ b/packages/ui/lib/components/FeedbackButton.tsx @@ -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 ( +
+ + +
+ ); +} diff --git a/packages/ui/lib/components/NewCouponForm.tsx b/packages/ui/lib/components/NewCouponForm.tsx new file mode 100644 index 0000000..4ce98d8 --- /dev/null +++ b/packages/ui/lib/components/NewCouponForm.tsx @@ -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 ( +
+ setCode(e.target.value)} required /> +