From mida-skills
Use when changing, reviewing, or adding code in the mida-cms repository — React/Redux frontend pages, components, hooks, services, API calls, Polaris UI, state management, Shopify auth flow, or shared packages (replayer, mcp).
How this skill is triggered — by the user, by Claude, or both
Slash command
/mida-skills:mida-cmsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill before modifying `mida-cms`. This is a **Shopify admin app** built as a Turborepo monorepo: a React SPA frontend + Node/Express backend + shared packages.
Use this skill before modifying mida-cms. This is a Shopify admin app built as a Turborepo monorepo: a React SPA frontend + Node/Express backend + shared packages.
ls mida-cms/web/frontend/
ls mida-cms/packages/
grep -rn "featureName\|componentName" mida-cms/web/frontend --include="*.jsx" --include="*.js"
mida-cms/
├── web/
│ ├── index.js # Express server entry (Shopify OAuth)
│ ├── auth/
│ │ └── afterAuth.js # Post-OAuth: generate JWT, call backend /shops/auth
│ └── frontend/ # React SPA (Vite)
│ ├── App.jsx # Root: providers + file-based routing
│ ├── index.jsx # createRoot entry
│ ├── pages/ # Route-level pages (auto-discovered via Vite glob)
│ ├── components/ # Reusable UI (Common/, Dashboard/, Analytics/, etc.)
│ ├── hooks/ # Custom React hooks
│ ├── services/ # API service layer
│ │ ├── api/ # *.service.js per domain
│ │ └── index.js # Barrel: export * as featureApi
│ ├── redux/ # State management
│ │ ├── reducers/ # Redux Toolkit slices
│ │ ├── actions/ # Action creators
│ │ └── sagas/ # Redux-Saga side effects
│ ├── helpers/ # *.helper.js (pure functions)
│ ├── hooks/ # useFeatureName.js
│ ├── consts/ # *.const.js
│ └── config/
│ ├── repository.config.js # HTTP client factory
│ └── index.js # Shopify app config
├── packages/
│ ├── @mida/mcp/ # MCP integration (TypeScript + Vite + TailwindCSS + Shadcn)
│ └── @mida/replayer/ # RRWeb session replay UI (Svelte + Rollup)
└── extensions/ # Shopify app extensions
ComponentName.jsx (PascalCase) or ComponentName/index.jsxentity.service.js (camelCase)useHookName.js (use prefix)feature.helper.jsfeature.reducer.js, Sagas: feature.saga.jsFeature.const.js (PascalCase)ComponentName.module.css or style.module.cssPath alias @/ → web/frontend/ root (configured in vite.config.js):
import { selectShop } from '@/redux/reducers/general.reducer';
import LoadingModal from '@/components/Common/LoadingModal';
import { sessionApi } from '@/services';
Barrel exports for services:
// services/index.js
export * as sessionApi from './api/session.service.js';
// Usage:
import { sessionApi, analyticApi } from '@/services';
await sessionApi.deleteSession({ ids, jwt });
// components/FeatureName/index.jsx
import { BlockStack, Card } from '@shopify/polaris';
import styles from './style.module.css';
import clsx from 'clsx';
export default function FeatureName({ prop1, onAction }) {
const [state, setState] = useState(null);
const handleAction = useCallback(() => {
// ...
}, []);
return (
<Card>
<BlockStack gap="400">
<div className={clsx(styles.container, state && styles.active)}>
{/* content */}
</div>
</BlockStack>
</Card>
);
}
clsx for conditional classes// services/api/feature.service.js
import { repositoryApi } from '@/config/repository.config';
export async function getFeatureData({ shopId, jwt }) {
const res = await repositoryApi.get(`/feature/${shopId}`, {
headers: { Authorization: `Bearer ${jwt}` }
});
const json = await res.json();
return { ok: json?.statusCode === 200, data: json?.payload };
}
export async function updateFeature({ id, data, jwt }) {
const res = await repositoryApi.put(`/feature/${id}`, data, {
headers: { Authorization: `Bearer ${jwt}` }
});
const json = await res.json();
return { ok: json?.statusCode === 200 };
}
Authorization: Bearer ${jwt} for authenticated endpointsrepositoryApi = main API; repositoryRecorder = recorder; repositoryHeatmap = heatmap{ ok, data, message } — never throw from service functionsservices/index.js barrelimport { fetchEventSource } from '@microsoft/fetch-event-source';
export const streamFeatureData = ({ params, signal, onEvent }) => {
return fetchEventSource(`${SERVER_URL}/feature/stream?${params}`, {
method: 'GET',
headers: { Accept: 'text/event-stream', Authorization: `Bearer ${jwt}` },
onmessage(event) {
if (event.data) onEvent(event.event || 'message', JSON.parse(event.data));
},
signal,
});
};
Slice (reducer + actions):
// redux/reducers/feature.reducer.js
const featureSlice = createSlice({
name: 'feature',
initialState: { data: null, loading: false, error: null },
reducers: {
setData: (state, action) => { state.data = action.payload; },
setLoading: (state, action) => { state.loading = action.payload; },
},
});
export const { setData, setLoading } = featureSlice.actions;
export const selectFeature = (state) => state.feature;
export default featureSlice;
Saga:
// redux/sagas/feature.saga.js
function* fetchFeatureData(action) {
try {
const jwt = action.payload;
const res = yield call(featureApi.getFeatureData, { jwt });
yield put(setData(res.data));
} catch (e) {
yield put(setError(e.message));
}
}
export default function* featureSaga() {
yield takeLatest(FETCH_FEATURE, fetchFeatureData);
}
Register in redux/sagas/index.js rootSaga with yield all([..., featureSaga()]).
// hooks/useFeatureSWR.js
import useSWR from 'swr';
export function useFeatureSWR({ shopId, startDate, endDate }) {
const key = shopId ? `/feature/${shopId}?start=${startDate}&end=${endDate}` : null;
const { data, error, isLoading } = useSWR(key, fetchData, { shouldRetryOnError: false });
return { data: data?.payload, error, isLoading };
}
JWT token is generated in web/auth/afterAuth.js after Shopify OAuth and attached to all API calls:
// In afterAuth.js (server-side)
const token = jwt.sign({ domain: shop.toLowerCase(), apiVersion }, jwtSecretKey);
// Token returned to frontend via redirect params
// In frontend — JWT stored in component state or redux
// All API calls:
const res = await repositoryApi.get('/data', { headers: { Authorization: `Bearer ${jwt}` } });
// pages/feature/index.jsx
import { Page, BlockStack } from '@shopify/polaris';
import FeatureHeader from '@/components/FeatureHeader';
import useFeatureSWR from '@/hooks/useFeatureSWR';
import { useSelector } from 'react-redux';
import { selectShop } from '@/redux/reducers/general.reducer';
export default function FeaturePage() {
const { jwt } = useSelector(selectShop);
const { data, isLoading } = useFeatureSWR({ jwt });
return (
<Page title="Feature">
<BlockStack gap="400">
<FeatureHeader data={data} loading={isLoading} />
</BlockStack>
</Page>
);
}
Pages are auto-discovered via import.meta.globEager('./pages/**/index.jsx') in App.jsx — no manual route registration.
components/FeatureName/index.jsx + style.module.cssservices/api/feature.service.js + export from services/index.jshooks/useFeatureName.js (SWR or plain React hooks)redux/reducers/feature.reducer.js + redux/sagas/feature.saga.jsredux/sagas/index.jspages/feature/index.jsx (auto-discovered)consts/Feature.const.jsWhen a task involves converting Figma designs to Shopify Polaris UI, use both MCPs in sequence:
The Figma MCP (mcp__claude_ai_Figma__*) is available in Claude Code. Authenticate first, then read the design:
mcp__claude_ai_Figma__authenticate if not yet connectedThe Shopify Dev MCP provides authoritative Polaris component guidance. Configure it in the project's .claude/settings.json:
{
"mcpServers": {
"shopify-dev-mcp": {
"command": "npx",
"args": ["-y", "@shopify/dev-mcp@latest"]
}
}
}
After configuration, use the MCP tools to:
| Figma concept | Polaris equivalent |
|---|---|
| Frame / top-level container | <Page> |
| Card / section | <Card> |
| Auto layout — vertical | <BlockStack gap="N"> |
| Auto layout — horizontal | <InlineStack gap="N"> |
| Divider / separator | <Divider> |
| Text (heading) | <Text as="h2" variant="headingMd"> |
| Text (body) | <Text as="p" variant="bodyMd"> |
| Text (label) | <Text as="span" variant="bodySm" tone="subdued"> |
| Primary button | <Button variant="primary"> |
| Secondary button | <Button> |
| Destructive button | <Button tone="critical"> |
| Text input | <TextField label="..." /> |
| Select / dropdown | <Select label="..." options={...} /> |
| Checkbox | <Checkbox label="..." /> |
| Toggle / switch | <Checkbox label="..." checked={...} /> |
| Badge / tag | <Badge tone="..."> |
| Banner / alert | <Banner tone="info|success|warning|critical"> |
| Data table | <DataTable> |
| Thumbnail / avatar | <Avatar> or <Thumbnail> |
| Spinner / loading | <Spinner> or <SkeletonBodyText> |
| Tooltip | <Tooltip content="..."> |
| Modal / dialog | <Modal> |
| Tabs | <Tabs> |
Spacing token mapping (Figma px → Polaris gap/space token):
| Figma spacing | Polaris token |
|---|---|
| 4px | "100" |
| 8px | "200" |
| 12px | "300" |
| 16px | "400" |
| 20px | "500" |
| 24px | "600" |
| 32px | "800" |
| 40px | "1000" |
| 48px | "1200" |
Generated components must:
@shopify/polaris 12 components — no custom layout primitivespx values in JSXclsx)var or class components — use functional components with hooksVITE_ prefix for frontend useSERVER_URL from env configcd mida-cms
pnpm run lint
pnpm run build # Check for TypeScript/build errors
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub quyensatoru/mida-md --plugin mida-skills