@fayz-ai/plugin-reputation 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Faya Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @fayz-ai/plugin-reputation
2
+
3
+ > Reviews and reputation, in one place — see the stars, reply, ask for more.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@fayz-ai/plugin-reputation.svg)](https://www.npmjs.com/package/@fayz-ai/plugin-reputation)
6
+ [![license](https://img.shields.io/npm/l/@fayz-ai/plugin-reputation.svg)](https://github.com/FayaLabs/fayz-sdk/blob/main/LICENSE)
7
+
8
+ Reputation is the cheapest growth lever a local business has, and the easiest to ignore. Reviews sit scattered across Google and Facebook, replies never happen, and nobody ever asks the happy customer for a rating. `plugin-reputation` is the GoHighLevel-style reputation surface for the Fayz engine: a single home for your rating, your review feed, and the actions that move it.
9
+
10
+ This plugin is an early scaffold. It ships a polished reputation home today — an average-rating card, a star distribution, a review feed with reply actions, and a "request reviews" entry point — running on built-in mock data so the surface is real to design and demo against. Live Google/Facebook review sync and automated review requests (over the SDK's connectors + automations) are the next milestone; the page is the placeholder they'll fill.
11
+
12
+ ## What's inside
13
+ - A `/reputation` home: average-rating card, star-distribution bars, and a review feed
14
+ - Per-review reply actions and a "Request reviews" call-to-action
15
+ - Google / Facebook review sources represented in the UI (mock data today)
16
+ - Configurable navigation (label, position, section) and vertical scoping
17
+
18
+ ## Install
19
+ ```bash
20
+ npm install @fayz-ai/plugin-reputation
21
+ ```
22
+ Peer deps: `@fayz-ai/core`, `@fayz-ai/ui`, plus `react` / `react-dom`.
23
+
24
+ ## Usage
25
+ ```tsx
26
+ import { defineSaas } from '@fayz-ai/saas'
27
+ import { createReputationPlugin } from '@fayz-ai/plugin-reputation'
28
+
29
+ export const app = defineSaas({
30
+ // ...
31
+ plugins: [
32
+ createReputationPlugin({ navLabel: 'Reputation', navPosition: 8 }),
33
+ ],
34
+ })
35
+ ```
36
+
37
+ ## Part of the Fayz SDK
38
+ The trust layer of the engine. Pairs with `plugin-conversations` (turn a great chat into a review request), `plugin-crm` (know exactly who to ask), and `plugin-marketing` (feed the social proof back into acquisition).
39
+
40
+ ## Roadmap & contributing
41
+ Early scaffold — built in the open. See the [Fayz SDK roadmap](../../docs/ROADMAP.md#plugin-reputation) for current gaps, missing features, and good first issues.
package/dist/index.cjs ADDED
@@ -0,0 +1,117 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var lucideReact = require('lucide-react');
5
+ var ui = require('@fayz-ai/ui');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var React__default = /*#__PURE__*/_interopDefault(React);
11
+
12
+ // src/index.ts
13
+ var REVIEWS = [
14
+ { id: "1", author: "Camila R.", source: "Google", rating: 5, text: "Absolutely loved the experience. The team is professional and friendly!", date: "Jun 15", replied: true },
15
+ { id: "2", author: "Tom B.", source: "Facebook", rating: 5, text: "Booking was effortless and the results were fantastic.", date: "Jun 13", replied: false },
16
+ { id: "3", author: "Aisha K.", source: "Google", rating: 4, text: "Great service overall, slightly long wait but worth it.", date: "Jun 11", replied: false },
17
+ { id: "4", author: "Marco P.", source: "Google", rating: 5, text: "Best in town. Highly recommend to anyone.", date: "Jun 9", replied: true },
18
+ { id: "5", author: "Lena S.", source: "Facebook", rating: 3, text: "Good but the pricing could be clearer up front.", date: "Jun 6", replied: false }
19
+ ];
20
+ var DISTRIBUTION = [
21
+ { stars: 5, count: 182 },
22
+ { stars: 4, count: 41 },
23
+ { stars: 3, count: 12 },
24
+ { stars: 2, count: 4 },
25
+ { stars: 1, count: 3 }
26
+ ];
27
+ function Stars({ rating, className }) {
28
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center gap-0.5 ${className ?? ""}`, children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Star, { className: `h-4 w-4 ${i <= rating ? "fill-amber-400 text-amber-400" : "text-muted-foreground/30"}` }, i)) });
29
+ }
30
+ function ReputationHome() {
31
+ const total = DISTRIBUTION.reduce((s, d) => s + d.count, 0);
32
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-6xl p-6", children: [
33
+ /* @__PURE__ */ jsxRuntime.jsx(ui.PageHeaderActions, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { children: [
34
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "mr-1.5 h-4 w-4" }),
35
+ " Request reviews"
36
+ ] }) }),
37
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3", children: [
38
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-card border border-border bg-card p-5 text-center", children: [
39
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-4xl font-bold text-foreground", children: "4.8" }),
40
+ /* @__PURE__ */ jsxRuntime.jsx(Stars, { rating: 5, className: "mt-1 justify-center" }),
41
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [
42
+ total,
43
+ " reviews"
44
+ ] })
45
+ ] }),
46
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-card border border-border bg-card p-5 lg:col-span-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: DISTRIBUTION.map((d) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
47
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "w-6 text-muted-foreground", children: [
48
+ d.stars,
49
+ "\u2605"
50
+ ] }),
51
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 flex-1 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full rounded-full bg-amber-400", style: { width: `${d.count / total * 100}%` } }) }),
52
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-8 text-right text-muted-foreground", children: d.count })
53
+ ] }, d.stars)) }) })
54
+ ] }),
55
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 space-y-3", children: REVIEWS.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-card border border-border bg-card p-4", children: [
56
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
57
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
58
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-muted text-sm font-semibold text-foreground", children: r.author[0] }),
59
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
60
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-foreground", children: r.author }),
61
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
62
+ r.source,
63
+ " \xB7 ",
64
+ r.date
65
+ ] })
66
+ ] })
67
+ ] }),
68
+ /* @__PURE__ */ jsxRuntime.jsx(Stars, { rating: r.rating })
69
+ ] }),
70
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: r.text }),
71
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: r.replied ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-emerald-600", children: "\u2713 Replied" }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", children: [
72
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "mr-1.5 h-3.5 w-3.5" }),
73
+ " Reply"
74
+ ] }) })
75
+ ] }, r.id)) }),
76
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 text-center text-xs text-muted-foreground", children: "Mock preview \xB7 Google/Facebook review sync + automated requests ship in a later milestone." })
77
+ ] });
78
+ }
79
+
80
+ // src/index.ts
81
+ function createReputationPlugin(options) {
82
+ const PageComponent = () => React__default.default.createElement(ReputationHome);
83
+ PageComponent.displayName = "ReputationHome";
84
+ return {
85
+ id: "reputation",
86
+ name: options?.navLabel ?? "Reputation",
87
+ icon: "Award",
88
+ version: "1.0.0",
89
+ scope: options?.scope ?? "universal",
90
+ verticalId: options?.verticalId,
91
+ defaultEnabled: true,
92
+ dependencies: [],
93
+ declaredFeatures: [{ id: "reputation", label: "Reputation", group: "Retain" }],
94
+ navigation: [
95
+ {
96
+ section: options?.navSection ?? "main",
97
+ position: options?.navPosition ?? 8,
98
+ label: options?.navLabel ?? "Reputation",
99
+ route: "/reputation",
100
+ icon: "Award",
101
+ permission: { feature: "reputation", action: "read" }
102
+ }
103
+ ],
104
+ routes: [
105
+ {
106
+ path: "/reputation",
107
+ component: PageComponent,
108
+ permission: { feature: "reputation", action: "read" }
109
+ }
110
+ ],
111
+ widgets: []
112
+ };
113
+ }
114
+
115
+ exports.createReputationPlugin = createReputationPlugin;
116
+ //# sourceMappingURL=index.cjs.map
117
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/views/ReputationHome.tsx","../src/index.ts"],"names":["jsx","Star","jsxs","PageHeaderActions","Button","Send","MessageSquare","React"],"mappings":";;;;;;;;;;;;AAcA,IAAM,OAAA,GAAoB;AAAA,EACxB,EAAE,EAAA,EAAI,GAAA,EAAK,MAAA,EAAQ,aAAa,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,CAAA,EAAG,IAAA,EAAM,yEAAA,EAA2E,IAAA,EAAM,QAAA,EAAU,SAAS,IAAA,EAAK;AAAA,EAC5K,EAAE,EAAA,EAAI,GAAA,EAAK,MAAA,EAAQ,UAAU,MAAA,EAAQ,UAAA,EAAY,MAAA,EAAQ,CAAA,EAAG,IAAA,EAAM,wDAAA,EAA0D,IAAA,EAAM,QAAA,EAAU,SAAS,KAAA,EAAM;AAAA,EAC3J,EAAE,EAAA,EAAI,GAAA,EAAK,MAAA,EAAQ,YAAY,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,CAAA,EAAG,IAAA,EAAM,yDAAA,EAA2D,IAAA,EAAM,QAAA,EAAU,SAAS,KAAA,EAAM;AAAA,EAC5J,EAAE,EAAA,EAAI,GAAA,EAAK,MAAA,EAAQ,YAAY,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,CAAA,EAAG,IAAA,EAAM,2CAAA,EAA6C,IAAA,EAAM,OAAA,EAAS,SAAS,IAAA,EAAK;AAAA,EAC5I,EAAE,EAAA,EAAI,GAAA,EAAK,MAAA,EAAQ,WAAW,MAAA,EAAQ,UAAA,EAAY,MAAA,EAAQ,CAAA,EAAG,IAAA,EAAM,iDAAA,EAAmD,IAAA,EAAM,OAAA,EAAS,SAAS,KAAA;AAChJ,CAAA;AAEA,IAAM,YAAA,GAAe;AAAA,EACnB,EAAE,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,GAAA,EAAI;AAAA,EACvB,EAAE,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,EAAA,EAAG;AAAA,EACtB,EAAE,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,EAAA,EAAG;AAAA,EACtB,EAAE,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,CAAA,EAAE;AAAA,EACrB,EAAE,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,CAAA;AACrB,CAAA;AAEA,SAAS,KAAA,CAAM,EAAE,MAAA,EAAQ,SAAA,EAAU,EAA2C;AAC5E,EAAA,uBACEA,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,CAAA,0BAAA,EAA6B,SAAA,IAAa,EAAE,CAAA,CAAA,EACzD,QAAA,EAAA,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,qBACpBA,cAAA,CAACC,gBAAA,EAAA,EAAa,SAAA,EAAW,CAAA,QAAA,EAAW,CAAA,IAAK,MAAA,GAAS,+BAAA,GAAkC,0BAA0B,CAAA,CAAA,EAAA,EAAnG,CAAuG,CACnH,CAAA,EACH,CAAA;AAEJ;AAEO,SAAS,cAAA,GAAiB;AAC/B,EAAA,MAAM,KAAA,GAAQ,aAAa,MAAA,CAAO,CAAC,GAAG,CAAA,KAAM,CAAA,GAAI,CAAA,CAAE,KAAA,EAAO,CAAC,CAAA;AAC1D,EAAA,uBACEC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EACb,QAAA,EAAA;AAAA,oBAAAF,cAAA,CAACG,oBAAA,EAAA,EACC,0CAACC,SAAA,EAAA,EAAO,QAAA,EAAA;AAAA,sBAAAJ,cAAA,CAACK,gBAAA,EAAA,EAAK,WAAU,gBAAA,EAAiB,CAAA;AAAA,MAAE;AAAA,KAAA,EAAgB,CAAA,EAC7D,CAAA;AAAA,oBAEAH,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,4CAAA,EACb,QAAA,EAAA;AAAA,sBAAAA,eAAA,CAAC,KAAA,EAAA,EAAI,WAAU,2DAAA,EACb,QAAA,EAAA;AAAA,wBAAAF,cAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,oCAAA,EAAqC,QAAA,EAAA,KAAA,EAAG,CAAA;AAAA,wBACrDA,cAAA,CAAC,KAAA,EAAA,EAAM,MAAA,EAAQ,CAAA,EAAG,WAAU,qBAAA,EAAsB,CAAA;AAAA,wBAClDE,eAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,oCAAA,EAAsC,QAAA,EAAA;AAAA,UAAA,KAAA;AAAA,UAAM;AAAA,SAAA,EAAQ;AAAA,OAAA,EACnE,CAAA;AAAA,sBACAF,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6DAAA,EACb,yCAAC,KAAA,EAAA,EAAI,SAAA,EAAU,aAAA,EACZ,QAAA,EAAA,YAAA,CAAa,IAAI,CAAC,CAAA,qBACjBE,eAAA,CAAC,KAAA,EAAA,EAAkB,WAAU,iCAAA,EAC3B,QAAA,EAAA;AAAA,wBAAAA,eAAA,CAAC,MAAA,EAAA,EAAK,WAAU,2BAAA,EAA6B,QAAA,EAAA;AAAA,UAAA,CAAA,CAAE,KAAA;AAAA,UAAM;AAAA,SAAA,EAAC,CAAA;AAAA,uCACrD,KAAA,EAAA,EAAI,SAAA,EAAU,oDACb,QAAA,kBAAAF,cAAA,CAAC,KAAA,EAAA,EAAI,WAAU,kCAAA,EAAmC,KAAA,EAAO,EAAE,KAAA,EAAO,GAAI,CAAA,CAAE,KAAA,GAAQ,QAAS,GAAG,CAAA,CAAA,CAAA,IAAO,CAAA,EACrG,CAAA;AAAA,wBACAA,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,sCAAA,EAAwC,YAAE,KAAA,EAAM;AAAA,OAAA,EAAA,EALxD,CAAA,CAAE,KAMZ,CACD,CAAA,EACH,CAAA,EACF;AAAA,KAAA,EACF,CAAA;AAAA,oBAEAA,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gBAAA,EACZ,QAAA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,qBACZE,eAAA,CAAC,KAAA,EAAA,EAAe,SAAA,EAAU,+CAAA,EACxB,QAAA,EAAA;AAAA,sBAAAA,eAAA,CAAC,KAAA,EAAA,EAAI,WAAU,kCAAA,EACb,QAAA,EAAA;AAAA,wBAAAA,eAAA,CAAC,KAAA,EAAA,EAAI,WAAU,yBAAA,EACb,QAAA,EAAA;AAAA,0BAAAF,cAAA,CAAC,SAAI,SAAA,EAAU,sGAAA,EACZ,QAAA,EAAA,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,EACb,CAAA;AAAA,0CACC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAAA,cAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,uCAAA,EAAyC,QAAA,EAAA,CAAA,CAAE,MAAA,EAAO,CAAA;AAAA,4BAC/DE,eAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,+BAAA,EAAiC,QAAA,EAAA;AAAA,cAAA,CAAA,CAAE,MAAA;AAAA,cAAO,QAAA;AAAA,cAAI,CAAA,CAAE;AAAA,aAAA,EAAK;AAAA,WAAA,EACpE;AAAA,SAAA,EACF,CAAA;AAAA,wBACAF,cAAA,CAAC,KAAA,EAAA,EAAM,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAQ;AAAA,OAAA,EAC3B,CAAA;AAAA,sBACAA,cAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,oCAAA,EAAsC,YAAE,IAAA,EAAK,CAAA;AAAA,qCACzD,KAAA,EAAA,EAAI,SAAA,EAAU,MAAA,EACZ,QAAA,EAAA,CAAA,CAAE,0BACDA,cAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,sCAAA,EAAuC,4BAAS,CAAA,mBAEhEE,eAAA,CAACE,aAAO,OAAA,EAAQ,SAAA,EAAU,MAAK,IAAA,EAAK,QAAA,EAAA;AAAA,wBAAAJ,cAAA,CAACM,yBAAA,EAAA,EAAc,WAAU,oBAAA,EAAqB,CAAA;AAAA,QAAE;AAAA,OAAA,EAAM,CAAA,EAE9F;AAAA,KAAA,EAAA,EApBQ,CAAA,CAAE,EAqBZ,CACD,CAAA,EACH,CAAA;AAAA,oBAEAN,cAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,gDAAA,EAAiD,QAAA,EAAA,+FAAA,EAE9D;AAAA,GAAA,EACF,CAAA;AAEJ;;;ACnFO,SAAS,uBAAuB,OAAA,EAAmD;AACxF,EAAA,MAAM,aAAA,GAA8C,MAAMO,sBAAA,CAAM,aAAA,CAAc,cAAc,CAAA;AAC5F,EAAA,aAAA,CAAc,WAAA,GAAc,gBAAA;AAE5B,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,YAAA;AAAA,IACJ,IAAA,EAAM,SAAS,QAAA,IAAY,YAAA;AAAA,IAC3B,IAAA,EAAM,OAAA;AAAA,IACN,OAAA,EAAS,OAAA;AAAA,IACT,KAAA,EAAO,SAAS,KAAA,IAAS,WAAA;AAAA,IACzB,YAAY,OAAA,EAAS,UAAA;AAAA,IACrB,cAAA,EAAgB,IAAA;AAAA,IAChB,cAAc,EAAC;AAAA,IACf,gBAAA,EAAkB,CAAC,EAAE,EAAA,EAAI,cAAc,KAAA,EAAO,YAAA,EAAc,KAAA,EAAO,QAAA,EAAU,CAAA;AAAA,IAC7E,UAAA,EAAY;AAAA,MACV;AAAA,QACE,OAAA,EAAS,SAAS,UAAA,IAAc,MAAA;AAAA,QAChC,QAAA,EAAU,SAAS,WAAA,IAAe,CAAA;AAAA,QAClC,KAAA,EAAO,SAAS,QAAA,IAAY,YAAA;AAAA,QAC5B,KAAA,EAAO,aAAA;AAAA,QACP,IAAA,EAAM,OAAA;AAAA,QACN,UAAA,EAAY,EAAE,OAAA,EAAS,YAAA,EAAc,QAAQ,MAAA;AAAgB;AAC/D,KACF;AAAA,IACA,MAAA,EAAQ;AAAA,MACN;AAAA,QACE,IAAA,EAAM,aAAA;AAAA,QACN,SAAA,EAAW,aAAA;AAAA,QACX,UAAA,EAAY,EAAE,OAAA,EAAS,YAAA,EAAc,QAAQ,MAAA;AAAgB;AAC/D,KACF;AAAA,IACA,SAAS;AAAC,GACZ;AACF","file":"index.cjs","sourcesContent":["import React from 'react'\nimport { Star, Send, MessageSquare } from 'lucide-react'\nimport { Button, PageHeaderActions } from '@fayz-ai/ui'\n\ninterface Review {\n id: string\n author: string\n source: 'Google' | 'Facebook'\n rating: number\n text: string\n date: string\n replied: boolean\n}\n\nconst REVIEWS: Review[] = [\n { id: '1', author: 'Camila R.', source: 'Google', rating: 5, text: 'Absolutely loved the experience. The team is professional and friendly!', date: 'Jun 15', replied: true },\n { id: '2', author: 'Tom B.', source: 'Facebook', rating: 5, text: 'Booking was effortless and the results were fantastic.', date: 'Jun 13', replied: false },\n { id: '3', author: 'Aisha K.', source: 'Google', rating: 4, text: 'Great service overall, slightly long wait but worth it.', date: 'Jun 11', replied: false },\n { id: '4', author: 'Marco P.', source: 'Google', rating: 5, text: 'Best in town. Highly recommend to anyone.', date: 'Jun 9', replied: true },\n { id: '5', author: 'Lena S.', source: 'Facebook', rating: 3, text: 'Good but the pricing could be clearer up front.', date: 'Jun 6', replied: false },\n]\n\nconst DISTRIBUTION = [\n { stars: 5, count: 182 },\n { stars: 4, count: 41 },\n { stars: 3, count: 12 },\n { stars: 2, count: 4 },\n { stars: 1, count: 3 },\n]\n\nfunction Stars({ rating, className }: { rating: number; className?: string }) {\n return (\n <div className={`flex items-center gap-0.5 ${className ?? ''}`}>\n {[1, 2, 3, 4, 5].map((i) => (\n <Star key={i} className={`h-4 w-4 ${i <= rating ? 'fill-amber-400 text-amber-400' : 'text-muted-foreground/30'}`} />\n ))}\n </div>\n )\n}\n\nexport function ReputationHome() {\n const total = DISTRIBUTION.reduce((s, d) => s + d.count, 0)\n return (\n <div className=\"mx-auto max-w-6xl p-6\">\n <PageHeaderActions>\n <Button><Send className=\"mr-1.5 h-4 w-4\" /> Request reviews</Button>\n </PageHeaderActions>\n\n <div className=\"mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"rounded-card border border-border bg-card p-5 text-center\">\n <p className=\"text-4xl font-bold text-foreground\">4.8</p>\n <Stars rating={5} className=\"mt-1 justify-center\" />\n <p className=\"mt-1 text-xs text-muted-foreground\">{total} reviews</p>\n </div>\n <div className=\"rounded-card border border-border bg-card p-5 lg:col-span-2\">\n <div className=\"space-y-1.5\">\n {DISTRIBUTION.map((d) => (\n <div key={d.stars} className=\"flex items-center gap-2 text-xs\">\n <span className=\"w-6 text-muted-foreground\">{d.stars}★</span>\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-muted\">\n <div className=\"h-full rounded-full bg-amber-400\" style={{ width: `${(d.count / total) * 100}%` }} />\n </div>\n <span className=\"w-8 text-right text-muted-foreground\">{d.count}</span>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n <div className=\"mt-6 space-y-3\">\n {REVIEWS.map((r) => (\n <div key={r.id} className=\"rounded-card border border-border bg-card p-4\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-muted text-sm font-semibold text-foreground\">\n {r.author[0]}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-foreground\">{r.author}</p>\n <p className=\"text-xs text-muted-foreground\">{r.source} · {r.date}</p>\n </div>\n </div>\n <Stars rating={r.rating} />\n </div>\n <p className=\"mt-2 text-sm text-muted-foreground\">{r.text}</p>\n <div className=\"mt-2\">\n {r.replied ? (\n <span className=\"text-xs font-medium text-emerald-600\">✓ Replied</span>\n ) : (\n <Button variant=\"outline\" size=\"sm\"><MessageSquare className=\"mr-1.5 h-3.5 w-3.5\" /> Reply</Button>\n )}\n </div>\n </div>\n ))}\n </div>\n\n <p className=\"mt-4 text-center text-xs text-muted-foreground\">\n Mock preview · Google/Facebook review sync + automated requests ship in a later milestone.\n </p>\n </div>\n )\n}\n","import React from 'react'\nimport type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { ReputationHome } from './views/ReputationHome'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-reputation — reviews & reputation (GoHighLevel \"Reputation\").\n// Universal plugin. M1 ships a rich mock home; Google/Facebook review sync +\n// automated review requests (over connectors + automations) come later.\n// ---------------------------------------------------------------------------\n\nexport interface ReputationPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n}\n\nexport function createReputationPlugin(options?: ReputationPluginOptions): PluginManifest {\n const PageComponent: React.ComponentType<unknown> = () => React.createElement(ReputationHome)\n PageComponent.displayName = 'ReputationHome'\n\n return {\n id: 'reputation',\n name: options?.navLabel ?? 'Reputation',\n icon: 'Award',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'reputation', label: 'Reputation', group: 'Retain' }],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 8,\n label: options?.navLabel ?? 'Reputation',\n route: '/reputation',\n icon: 'Award',\n permission: { feature: 'reputation', action: 'read' as const },\n },\n ],\n routes: [\n {\n path: '/reputation',\n component: PageComponent,\n permission: { feature: 'reputation', action: 'read' as const },\n },\n ],\n widgets: [],\n }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core';
2
+ export interface ReputationPluginOptions {
3
+ navPosition?: number;
4
+ navSection?: 'main' | 'secondary' | 'settings';
5
+ navLabel?: string;
6
+ scope?: PluginScope;
7
+ verticalId?: VerticalId;
8
+ }
9
+ export declare function createReputationPlugin(options?: ReputationPluginOptions): PluginManifest;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAS5E,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB;AAED,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,cAAc,CAiCxF"}
package/dist/index.js ADDED
@@ -0,0 +1,111 @@
1
+ import React from 'react';
2
+ import { Send, MessageSquare, Star } from 'lucide-react';
3
+ import { PageHeaderActions, Button } from '@fayz-ai/ui';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ // src/index.ts
7
+ var REVIEWS = [
8
+ { id: "1", author: "Camila R.", source: "Google", rating: 5, text: "Absolutely loved the experience. The team is professional and friendly!", date: "Jun 15", replied: true },
9
+ { id: "2", author: "Tom B.", source: "Facebook", rating: 5, text: "Booking was effortless and the results were fantastic.", date: "Jun 13", replied: false },
10
+ { id: "3", author: "Aisha K.", source: "Google", rating: 4, text: "Great service overall, slightly long wait but worth it.", date: "Jun 11", replied: false },
11
+ { id: "4", author: "Marco P.", source: "Google", rating: 5, text: "Best in town. Highly recommend to anyone.", date: "Jun 9", replied: true },
12
+ { id: "5", author: "Lena S.", source: "Facebook", rating: 3, text: "Good but the pricing could be clearer up front.", date: "Jun 6", replied: false }
13
+ ];
14
+ var DISTRIBUTION = [
15
+ { stars: 5, count: 182 },
16
+ { stars: 4, count: 41 },
17
+ { stars: 3, count: 12 },
18
+ { stars: 2, count: 4 },
19
+ { stars: 1, count: 3 }
20
+ ];
21
+ function Stars({ rating, className }) {
22
+ return /* @__PURE__ */ jsx("div", { className: `flex items-center gap-0.5 ${className ?? ""}`, children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx(Star, { className: `h-4 w-4 ${i <= rating ? "fill-amber-400 text-amber-400" : "text-muted-foreground/30"}` }, i)) });
23
+ }
24
+ function ReputationHome() {
25
+ const total = DISTRIBUTION.reduce((s, d) => s + d.count, 0);
26
+ return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl p-6", children: [
27
+ /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsxs(Button, { children: [
28
+ /* @__PURE__ */ jsx(Send, { className: "mr-1.5 h-4 w-4" }),
29
+ " Request reviews"
30
+ ] }) }),
31
+ /* @__PURE__ */ jsxs("div", { className: "mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3", children: [
32
+ /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card p-5 text-center", children: [
33
+ /* @__PURE__ */ jsx("p", { className: "text-4xl font-bold text-foreground", children: "4.8" }),
34
+ /* @__PURE__ */ jsx(Stars, { rating: 5, className: "mt-1 justify-center" }),
35
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [
36
+ total,
37
+ " reviews"
38
+ ] })
39
+ ] }),
40
+ /* @__PURE__ */ jsx("div", { className: "rounded-card border border-border bg-card p-5 lg:col-span-2", children: /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: DISTRIBUTION.map((d) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
41
+ /* @__PURE__ */ jsxs("span", { className: "w-6 text-muted-foreground", children: [
42
+ d.stars,
43
+ "\u2605"
44
+ ] }),
45
+ /* @__PURE__ */ jsx("div", { className: "h-2 flex-1 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx("div", { className: "h-full rounded-full bg-amber-400", style: { width: `${d.count / total * 100}%` } }) }),
46
+ /* @__PURE__ */ jsx("span", { className: "w-8 text-right text-muted-foreground", children: d.count })
47
+ ] }, d.stars)) }) })
48
+ ] }),
49
+ /* @__PURE__ */ jsx("div", { className: "mt-6 space-y-3", children: REVIEWS.map((r) => /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card p-4", children: [
50
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
51
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
52
+ /* @__PURE__ */ jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-muted text-sm font-semibold text-foreground", children: r.author[0] }),
53
+ /* @__PURE__ */ jsxs("div", { children: [
54
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: r.author }),
55
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
56
+ r.source,
57
+ " \xB7 ",
58
+ r.date
59
+ ] })
60
+ ] })
61
+ ] }),
62
+ /* @__PURE__ */ jsx(Stars, { rating: r.rating })
63
+ ] }),
64
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: r.text }),
65
+ /* @__PURE__ */ jsx("div", { className: "mt-2", children: r.replied ? /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-emerald-600", children: "\u2713 Replied" }) : /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", children: [
66
+ /* @__PURE__ */ jsx(MessageSquare, { className: "mr-1.5 h-3.5 w-3.5" }),
67
+ " Reply"
68
+ ] }) })
69
+ ] }, r.id)) }),
70
+ /* @__PURE__ */ jsx("p", { className: "mt-4 text-center text-xs text-muted-foreground", children: "Mock preview \xB7 Google/Facebook review sync + automated requests ship in a later milestone." })
71
+ ] });
72
+ }
73
+
74
+ // src/index.ts
75
+ function createReputationPlugin(options) {
76
+ const PageComponent = () => React.createElement(ReputationHome);
77
+ PageComponent.displayName = "ReputationHome";
78
+ return {
79
+ id: "reputation",
80
+ name: options?.navLabel ?? "Reputation",
81
+ icon: "Award",
82
+ version: "1.0.0",
83
+ scope: options?.scope ?? "universal",
84
+ verticalId: options?.verticalId,
85
+ defaultEnabled: true,
86
+ dependencies: [],
87
+ declaredFeatures: [{ id: "reputation", label: "Reputation", group: "Retain" }],
88
+ navigation: [
89
+ {
90
+ section: options?.navSection ?? "main",
91
+ position: options?.navPosition ?? 8,
92
+ label: options?.navLabel ?? "Reputation",
93
+ route: "/reputation",
94
+ icon: "Award",
95
+ permission: { feature: "reputation", action: "read" }
96
+ }
97
+ ],
98
+ routes: [
99
+ {
100
+ path: "/reputation",
101
+ component: PageComponent,
102
+ permission: { feature: "reputation", action: "read" }
103
+ }
104
+ ],
105
+ widgets: []
106
+ };
107
+ }
108
+
109
+ export { createReputationPlugin };
110
+ //# sourceMappingURL=index.js.map
111
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/views/ReputationHome.tsx","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import React from 'react'\nimport { Star, Send, MessageSquare } from 'lucide-react'\nimport { Button, PageHeaderActions } from '@fayz-ai/ui'\n\ninterface Review {\n id: string\n author: string\n source: 'Google' | 'Facebook'\n rating: number\n text: string\n date: string\n replied: boolean\n}\n\nconst REVIEWS: Review[] = [\n { id: '1', author: 'Camila R.', source: 'Google', rating: 5, text: 'Absolutely loved the experience. The team is professional and friendly!', date: 'Jun 15', replied: true },\n { id: '2', author: 'Tom B.', source: 'Facebook', rating: 5, text: 'Booking was effortless and the results were fantastic.', date: 'Jun 13', replied: false },\n { id: '3', author: 'Aisha K.', source: 'Google', rating: 4, text: 'Great service overall, slightly long wait but worth it.', date: 'Jun 11', replied: false },\n { id: '4', author: 'Marco P.', source: 'Google', rating: 5, text: 'Best in town. Highly recommend to anyone.', date: 'Jun 9', replied: true },\n { id: '5', author: 'Lena S.', source: 'Facebook', rating: 3, text: 'Good but the pricing could be clearer up front.', date: 'Jun 6', replied: false },\n]\n\nconst DISTRIBUTION = [\n { stars: 5, count: 182 },\n { stars: 4, count: 41 },\n { stars: 3, count: 12 },\n { stars: 2, count: 4 },\n { stars: 1, count: 3 },\n]\n\nfunction Stars({ rating, className }: { rating: number; className?: string }) {\n return (\n <div className={`flex items-center gap-0.5 ${className ?? ''}`}>\n {[1, 2, 3, 4, 5].map((i) => (\n <Star key={i} className={`h-4 w-4 ${i <= rating ? 'fill-amber-400 text-amber-400' : 'text-muted-foreground/30'}`} />\n ))}\n </div>\n )\n}\n\nexport function ReputationHome() {\n const total = DISTRIBUTION.reduce((s, d) => s + d.count, 0)\n return (\n <div className=\"mx-auto max-w-6xl p-6\">\n <PageHeaderActions>\n <Button><Send className=\"mr-1.5 h-4 w-4\" /> Request reviews</Button>\n </PageHeaderActions>\n\n <div className=\"mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"rounded-card border border-border bg-card p-5 text-center\">\n <p className=\"text-4xl font-bold text-foreground\">4.8</p>\n <Stars rating={5} className=\"mt-1 justify-center\" />\n <p className=\"mt-1 text-xs text-muted-foreground\">{total} reviews</p>\n </div>\n <div className=\"rounded-card border border-border bg-card p-5 lg:col-span-2\">\n <div className=\"space-y-1.5\">\n {DISTRIBUTION.map((d) => (\n <div key={d.stars} className=\"flex items-center gap-2 text-xs\">\n <span className=\"w-6 text-muted-foreground\">{d.stars}★</span>\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-muted\">\n <div className=\"h-full rounded-full bg-amber-400\" style={{ width: `${(d.count / total) * 100}%` }} />\n </div>\n <span className=\"w-8 text-right text-muted-foreground\">{d.count}</span>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n <div className=\"mt-6 space-y-3\">\n {REVIEWS.map((r) => (\n <div key={r.id} className=\"rounded-card border border-border bg-card p-4\">\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-muted text-sm font-semibold text-foreground\">\n {r.author[0]}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-foreground\">{r.author}</p>\n <p className=\"text-xs text-muted-foreground\">{r.source} · {r.date}</p>\n </div>\n </div>\n <Stars rating={r.rating} />\n </div>\n <p className=\"mt-2 text-sm text-muted-foreground\">{r.text}</p>\n <div className=\"mt-2\">\n {r.replied ? (\n <span className=\"text-xs font-medium text-emerald-600\">✓ Replied</span>\n ) : (\n <Button variant=\"outline\" size=\"sm\"><MessageSquare className=\"mr-1.5 h-3.5 w-3.5\" /> Reply</Button>\n )}\n </div>\n </div>\n ))}\n </div>\n\n <p className=\"mt-4 text-center text-xs text-muted-foreground\">\n Mock preview · Google/Facebook review sync + automated requests ship in a later milestone.\n </p>\n </div>\n )\n}\n","import React from 'react'\nimport type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'\nimport { ReputationHome } from './views/ReputationHome'\n\n// ---------------------------------------------------------------------------\n// @fayz-ai/plugin-reputation — reviews & reputation (GoHighLevel \"Reputation\").\n// Universal plugin. M1 ships a rich mock home; Google/Facebook review sync +\n// automated review requests (over connectors + automations) come later.\n// ---------------------------------------------------------------------------\n\nexport interface ReputationPluginOptions {\n navPosition?: number\n navSection?: 'main' | 'secondary' | 'settings'\n navLabel?: string\n scope?: PluginScope\n verticalId?: VerticalId\n}\n\nexport function createReputationPlugin(options?: ReputationPluginOptions): PluginManifest {\n const PageComponent: React.ComponentType<unknown> = () => React.createElement(ReputationHome)\n PageComponent.displayName = 'ReputationHome'\n\n return {\n id: 'reputation',\n name: options?.navLabel ?? 'Reputation',\n icon: 'Award',\n version: '1.0.0',\n scope: options?.scope ?? 'universal',\n verticalId: options?.verticalId,\n defaultEnabled: true,\n dependencies: [],\n declaredFeatures: [{ id: 'reputation', label: 'Reputation', group: 'Retain' }],\n navigation: [\n {\n section: options?.navSection ?? 'main',\n position: options?.navPosition ?? 8,\n label: options?.navLabel ?? 'Reputation',\n route: '/reputation',\n icon: 'Award',\n permission: { feature: 'reputation', action: 'read' as const },\n },\n ],\n routes: [\n {\n path: '/reputation',\n component: PageComponent,\n permission: { feature: 'reputation', action: 'read' as const },\n },\n ],\n widgets: [],\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function ReputationHome(): React.JSX.Element;
3
+ //# sourceMappingURL=ReputationHome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReputationHome.d.ts","sourceRoot":"","sources":["../../src/views/ReputationHome.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAwCzB,wBAAgB,cAAc,sBA6D7B"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@fayz-ai/plugin-reputation",
3
+ "version": "0.2.0",
4
+ "description": "Fayz SDK — reviews & reputation management plugin",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "source": "./src/index.ts",
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "src"
20
+ ],
21
+ "peerDependencies": {
22
+ "react": "^18.0.0 || ^19.0.0",
23
+ "react-dom": "^18.0.0 || ^19.0.0"
24
+ },
25
+ "dependencies": {
26
+ "lucide-react": "^0.400.0",
27
+ "@fayz-ai/core": "^0.2.0",
28
+ "@fayz-ai/ui": "^0.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/react": "^18.3.0",
32
+ "react": "^18.3.0",
33
+ "tsup": "^8.2.0",
34
+ "typescript": "^5.5.0",
35
+ "@types/react-dom": "^18.3.0"
36
+ },
37
+ "license": "MIT",
38
+ "keywords": [
39
+ "fayz",
40
+ "fayz-plugin",
41
+ "fayz-sdk"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup && tsc --emitDeclarationOnly --declaration --declarationMap --noEmit false",
48
+ "dev": "tsup --watch",
49
+ "typecheck": "tsc --noEmit",
50
+ "clean": "rm -rf dist"
51
+ }
52
+ }
package/src/index.ts ADDED
@@ -0,0 +1,52 @@
1
+ import React from 'react'
2
+ import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'
3
+ import { ReputationHome } from './views/ReputationHome'
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // @fayz-ai/plugin-reputation — reviews & reputation (GoHighLevel "Reputation").
7
+ // Universal plugin. M1 ships a rich mock home; Google/Facebook review sync +
8
+ // automated review requests (over connectors + automations) come later.
9
+ // ---------------------------------------------------------------------------
10
+
11
+ export interface ReputationPluginOptions {
12
+ navPosition?: number
13
+ navSection?: 'main' | 'secondary' | 'settings'
14
+ navLabel?: string
15
+ scope?: PluginScope
16
+ verticalId?: VerticalId
17
+ }
18
+
19
+ export function createReputationPlugin(options?: ReputationPluginOptions): PluginManifest {
20
+ const PageComponent: React.ComponentType<unknown> = () => React.createElement(ReputationHome)
21
+ PageComponent.displayName = 'ReputationHome'
22
+
23
+ return {
24
+ id: 'reputation',
25
+ name: options?.navLabel ?? 'Reputation',
26
+ icon: 'Award',
27
+ version: '1.0.0',
28
+ scope: options?.scope ?? 'universal',
29
+ verticalId: options?.verticalId,
30
+ defaultEnabled: true,
31
+ dependencies: [],
32
+ declaredFeatures: [{ id: 'reputation', label: 'Reputation', group: 'Retain' }],
33
+ navigation: [
34
+ {
35
+ section: options?.navSection ?? 'main',
36
+ position: options?.navPosition ?? 8,
37
+ label: options?.navLabel ?? 'Reputation',
38
+ route: '/reputation',
39
+ icon: 'Award',
40
+ permission: { feature: 'reputation', action: 'read' as const },
41
+ },
42
+ ],
43
+ routes: [
44
+ {
45
+ path: '/reputation',
46
+ component: PageComponent,
47
+ permission: { feature: 'reputation', action: 'read' as const },
48
+ },
49
+ ],
50
+ widgets: [],
51
+ }
52
+ }
@@ -0,0 +1,102 @@
1
+ import React from 'react'
2
+ import { Star, Send, MessageSquare } from 'lucide-react'
3
+ import { Button, PageHeaderActions } from '@fayz-ai/ui'
4
+
5
+ interface Review {
6
+ id: string
7
+ author: string
8
+ source: 'Google' | 'Facebook'
9
+ rating: number
10
+ text: string
11
+ date: string
12
+ replied: boolean
13
+ }
14
+
15
+ const REVIEWS: Review[] = [
16
+ { id: '1', author: 'Camila R.', source: 'Google', rating: 5, text: 'Absolutely loved the experience. The team is professional and friendly!', date: 'Jun 15', replied: true },
17
+ { id: '2', author: 'Tom B.', source: 'Facebook', rating: 5, text: 'Booking was effortless and the results were fantastic.', date: 'Jun 13', replied: false },
18
+ { id: '3', author: 'Aisha K.', source: 'Google', rating: 4, text: 'Great service overall, slightly long wait but worth it.', date: 'Jun 11', replied: false },
19
+ { id: '4', author: 'Marco P.', source: 'Google', rating: 5, text: 'Best in town. Highly recommend to anyone.', date: 'Jun 9', replied: true },
20
+ { id: '5', author: 'Lena S.', source: 'Facebook', rating: 3, text: 'Good but the pricing could be clearer up front.', date: 'Jun 6', replied: false },
21
+ ]
22
+
23
+ const DISTRIBUTION = [
24
+ { stars: 5, count: 182 },
25
+ { stars: 4, count: 41 },
26
+ { stars: 3, count: 12 },
27
+ { stars: 2, count: 4 },
28
+ { stars: 1, count: 3 },
29
+ ]
30
+
31
+ function Stars({ rating, className }: { rating: number; className?: string }) {
32
+ return (
33
+ <div className={`flex items-center gap-0.5 ${className ?? ''}`}>
34
+ {[1, 2, 3, 4, 5].map((i) => (
35
+ <Star key={i} className={`h-4 w-4 ${i <= rating ? 'fill-amber-400 text-amber-400' : 'text-muted-foreground/30'}`} />
36
+ ))}
37
+ </div>
38
+ )
39
+ }
40
+
41
+ export function ReputationHome() {
42
+ const total = DISTRIBUTION.reduce((s, d) => s + d.count, 0)
43
+ return (
44
+ <div className="mx-auto max-w-6xl p-6">
45
+ <PageHeaderActions>
46
+ <Button><Send className="mr-1.5 h-4 w-4" /> Request reviews</Button>
47
+ </PageHeaderActions>
48
+
49
+ <div className="mt-6 grid grid-cols-1 gap-4 lg:grid-cols-3">
50
+ <div className="rounded-card border border-border bg-card p-5 text-center">
51
+ <p className="text-4xl font-bold text-foreground">4.8</p>
52
+ <Stars rating={5} className="mt-1 justify-center" />
53
+ <p className="mt-1 text-xs text-muted-foreground">{total} reviews</p>
54
+ </div>
55
+ <div className="rounded-card border border-border bg-card p-5 lg:col-span-2">
56
+ <div className="space-y-1.5">
57
+ {DISTRIBUTION.map((d) => (
58
+ <div key={d.stars} className="flex items-center gap-2 text-xs">
59
+ <span className="w-6 text-muted-foreground">{d.stars}★</span>
60
+ <div className="h-2 flex-1 overflow-hidden rounded-full bg-muted">
61
+ <div className="h-full rounded-full bg-amber-400" style={{ width: `${(d.count / total) * 100}%` }} />
62
+ </div>
63
+ <span className="w-8 text-right text-muted-foreground">{d.count}</span>
64
+ </div>
65
+ ))}
66
+ </div>
67
+ </div>
68
+ </div>
69
+
70
+ <div className="mt-6 space-y-3">
71
+ {REVIEWS.map((r) => (
72
+ <div key={r.id} className="rounded-card border border-border bg-card p-4">
73
+ <div className="flex items-start justify-between">
74
+ <div className="flex items-center gap-3">
75
+ <div className="flex h-9 w-9 items-center justify-center rounded-full bg-muted text-sm font-semibold text-foreground">
76
+ {r.author[0]}
77
+ </div>
78
+ <div>
79
+ <p className="text-sm font-semibold text-foreground">{r.author}</p>
80
+ <p className="text-xs text-muted-foreground">{r.source} · {r.date}</p>
81
+ </div>
82
+ </div>
83
+ <Stars rating={r.rating} />
84
+ </div>
85
+ <p className="mt-2 text-sm text-muted-foreground">{r.text}</p>
86
+ <div className="mt-2">
87
+ {r.replied ? (
88
+ <span className="text-xs font-medium text-emerald-600">✓ Replied</span>
89
+ ) : (
90
+ <Button variant="outline" size="sm"><MessageSquare className="mr-1.5 h-3.5 w-3.5" /> Reply</Button>
91
+ )}
92
+ </div>
93
+ </div>
94
+ ))}
95
+ </div>
96
+
97
+ <p className="mt-4 text-center text-xs text-muted-foreground">
98
+ Mock preview · Google/Facebook review sync + automated requests ship in a later milestone.
99
+ </p>
100
+ </div>
101
+ )
102
+ }