# thinkrank/1.0.0/src/admin/stores/metadata.js

ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console &amp; Local SEO, version 1.0.0. 214 lines.

- Page: https://pluginprobe.com/plugins/thinkrank/1.0.0/code/src/admin/stores/metadata.js
- Raw: https://pluginprobe.com/plugins/thinkrank/1.0.0/raw/src/admin/stores/metadata.js
- Modified: 2025-08-10T07:57:44+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/thinkrank/1.0.0/code/src/admin/stores/metadata.js#L10-L20`.

```javascript
/**
 * Metadata Data Store
 * 
 * Manages AI-generated metadata for posts and pages
 * 
 * @package ThinkRank
 * @since 1.0.0
 */

import { createReduxStore } from '@wordpress/data';
import apiFetch from '@wordpress/api-fetch';

// Initial state
const DEFAULT_STATE = {
    metadata: {},
    isGenerating: {},
    generationHistory: {},
    isLoading: false,
};

// Action types
const TYPES = {
    SET_METADATA: 'SET_METADATA',
    SET_GENERATING: 'SET_GENERATING',
    SET_LOADING: 'SET_LOADING',
    ADD_TO_HISTORY: 'ADD_TO_HISTORY',
    CLEAR_METADATA: 'CLEAR_METADATA',
};

// Reducer
const reducer = (state = DEFAULT_STATE, action) => {
    switch (action.type) {
        case TYPES.SET_METADATA:
            return {
                ...state,
                metadata: {
                    ...state.metadata,
                    [action.postId]: action.metadata,
                },
            };

        case TYPES.SET_GENERATING:
            return {
                ...state,
                isGenerating: {
                    ...state.isGenerating,
                    [action.postId]: action.generating,
                },
            };

        case TYPES.SET_LOADING:
            return {
                ...state,
                isLoading: action.loading,
            };

        case TYPES.ADD_TO_HISTORY:
            return {
                ...state,
                generationHistory: {
                    ...state.generationHistory,
                    [action.postId]: [
                        ...(state.generationHistory[action.postId] || []),
                        action.entry,
                    ],
                },
            };

        case TYPES.CLEAR_METADATA:
            return {
                ...state,
                metadata: {
                    ...state.metadata,
                    [action.postId]: null,
                },
            };

        default:
            return state;
    }
};

// Actions
const actions = {
    setMetadata(postId, metadata) {
        return {
            type: TYPES.SET_METADATA,
            postId,
            metadata,
        };
    },

    setGenerating(postId, generating) {
        return {
            type: TYPES.SET_GENERATING,
            postId,
            generating,
        };
    },

    setLoading(loading) {
        return {
            type: TYPES.SET_LOADING,
            loading,
        };
    },

    addToHistory(postId, entry) {
        return {
            type: TYPES.ADD_TO_HISTORY,
            postId,
            entry,
        };
    },

    clearMetadata(postId) {
        return {
            type: TYPES.CLEAR_METADATA,
            postId,
        };
    },

    // Async actions (placeholders for Sprint 2)
    *generateMetadata(postId, options = {}) {
        yield actions.setGenerating(postId, true);

        try {
            // This will be implemented in Sprint 2
            const metadata = {
                title: 'AI Generated Title',
                description: 'AI Generated Description',
                generated_at: new Date().toISOString(),
            };

            yield actions.setMetadata(postId, metadata);
            yield actions.addToHistory(postId, {
                timestamp: Date.now(),
                action: 'generated',
                metadata,
            });

            return metadata;

        } catch (error) {
            console.error('Failed to generate metadata:', error);
            throw error;
        } finally {
            yield actions.setGenerating(postId, false);
        }
    },

    *loadMetadata(postId) {
        yield actions.setLoading(true);

        try {
            // This will be implemented in Sprint 2
            const metadata = yield apiFetch({
                path: `/thinkrank/v1/metadata/${postId}`,
            });

            yield actions.setMetadata(postId, metadata);
            return metadata;

        } catch (error) {
            console.error('Failed to load metadata:', error);
        } finally {
            yield actions.setLoading(false);
        }
    },
};

// Selectors
const selectors = {
    getMetadata(state, postId) {
        return state.metadata[postId] || null;
    },

    getIsGenerating(state, postId) {
        return state.isGenerating[postId] || false;
    },

    getIsLoading(state) {
        return state.isLoading;
    },

    getGenerationHistory(state, postId) {
        return state.generationHistory[postId] || [];
    },

    // Computed selectors
    hasMetadata(state, postId) {
        return !!state.metadata[postId];
    },

    getMetadataTitle(state, postId) {
        const metadata = state.metadata[postId];
        return metadata?.title || '';
    },

    getMetadataDescription(state, postId) {
        const metadata = state.metadata[postId];
        return metadata?.description || '';
    },
};

// Create and export store
const store = createReduxStore('thinkrank/metadata', {
    reducer,
    actions,
    selectors,
});

export default store;

```
