-
Notifications
You must be signed in to change notification settings - Fork 30
feat(rating): add RatingStars, RatingModal; integrate maintainer rati… #75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
09e49c6
0eb6416
fa54563
93af1fd
d216b3f
a0bfcdd
e6ab1d5
38d0d8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use client" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useMemo, useState } from "react" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { RatingModal } from "../rating/rating-modal" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Button } from "@/components/ui/button" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Separator } from "@/components/ui/separator" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { Bounty } from "@/types/bounty" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -20,8 +21,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [loading, setLoading] = useState(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // const router = useRouter() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Mock user ID for now - in real app this comes from auth context | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Mock user ID and maintainer check for now - in real app this comes from auth context | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const CURRENT_USER_ID = "mock-user-123" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const IS_MAINTAINER = true // TODO: Replace with real maintainer check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // const isClaimable = bounty.status === "open" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -75,7 +77,61 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Rating modal state | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [showRating, setShowRating] = useState(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [completed, setCompleted] = useState(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [lastRating, setLastRating] = useState<number | null>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [reputationGain, setReputationGain] = useState<number | null>(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [hasRated, setHasRated] = useState(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleMarkCompleted = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!IS_MAINTAINER) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert('Only maintainers can mark as completed.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setLoading(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Simulate completion API call | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setLoading(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setCompleted(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setShowRating(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, 1000) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleSubmitRating = async (rating: number, feedback: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (hasRated) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert('You have already rated this contributor.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!IS_MAINTAINER) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert('Only maintainers can rate contributors.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!completed) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert('Bounty must be marked as completed before rating.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+114
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -name "bounty-sidebar.tsx" -type fRepository: boundlessfi/bounties Length of output: 102 🏁 Script executed: find . -name "*rating*modal*" -o -name "*RatingModal*" | grep -E "\.(tsx?|jsx?)$"Repository: boundlessfi/bounties Length of output: 100 🏁 Script executed: find . -type f -name "*.tsx" | xargs grep -l "RatingModal" | head -20Repository: boundlessfi/bounties Length of output: 139 🏁 Script executed: wc -l ./components/bounty/bounty-sidebar.tsxRepository: boundlessfi/bounties Length of output: 106 🏁 Script executed: cat -n ./components/bounty/bounty-sidebar.tsx | grep -A 50 "handleSubmitRating"Repository: boundlessfi/bounties Length of output: 4539 🏁 Script executed: wc -l ./components/rating/rating-modal.tsxRepository: boundlessfi/bounties Length of output: 103 🏁 Script executed: cat -n ./components/rating/rating-modal.tsxRepository: boundlessfi/bounties Length of output: 2888 Guard failures should reject so the modal doesn't show success.
🛠️ Suggested fix if (hasRated) {
alert('You have already rated this contributor.');
- return;
+ throw new Error('You have already rated this contributor.');
}
if (!IS_MAINTAINER) {
alert('Only maintainers can rate contributors.');
- return;
+ throw new Error('Only maintainers can rate contributors.');
}
if (!completed) {
alert('Bounty must be marked as completed before rating.');
- return;
+ throw new Error('Bounty must be marked as completed before rating.');
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Simulate API call to reputation endpoint and calculate points | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await new Promise((res) => setTimeout(res, 1000)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setLastRating(rating) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setReputationGain(rating * 10) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setHasRated(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setShowRating(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Notify contributor (mock) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toast.success(`You have been rated ${rating} star${rating > 1 ? 's' : ''} and gained +${rating * 10} reputation!`, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: 'Congratulations on your contribution!' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const renderActionButton = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (bounty.status === 'claimed' && IS_MAINTAINER && !completed) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button onClick={handleMarkCompleted} disabled={loading} className="w-full gap-2 bg-green-600 text-white hover:bg-green-700"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {loading ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Check className="mr-2 h-4 w-4" />} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Mark as Completed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
141
to
+149
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allow reopening the rating flow after completion. Once the modal is dismissed, there’s no way to rate later because the “Mark as Completed” button disappears and no “Rate Contributor” action is shown. This blocks a core flow if the maintainer closes the modal accidentally. 🛠️ Suggested fix (add a “Rate Contributor” action) const renderActionButton = () => {
if (bounty.status === 'claimed' && IS_MAINTAINER && !completed) {
return (
<Button onClick={handleMarkCompleted} disabled={loading} className="w-full gap-2 bg-green-600 text-white hover:bg-green-700">
{loading ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Check className="mr-2 h-4 w-4" />}
Mark as Completed
</Button>
)
}
+ if (bounty.status === 'claimed' && IS_MAINTAINER && completed && !hasRated) {
+ return (
+ <Button onClick={() => setShowRating(true)} className="w-full gap-2 bg-primary text-primary-foreground hover:bg-primary/90">
+ Rate Contributor
+ </Button>
+ )
+ }🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (bounty.status !== 'open') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const labels: Record<string, string> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| claimed: 'Already Claimed', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -155,6 +211,23 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="sticky top-4 rounded-xl border border-gray-800 bg-background-card p-6 space-y-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* Sidebar UI */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {showRating && !hasRated && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <RatingModal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor={{ id: bounty.claimedBy || '', name: 'Contributor', reputation: 100 + (reputationGain || 0) }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bounty={{ id: bounty.id, title: bounty.issueTitle }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onSubmit={handleSubmitRating} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClose={() => setShowRating(false)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* Show rating and reputation gain after rating, visible to all users if available */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {lastRating && reputationGain && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="p-4 mb-4 rounded bg-green-900/60 text-green-200 border border-green-700"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="mb-1">{IS_MAINTAINER ? 'You rated the contributor:' : 'Contributor was rated:'} <b>{lastRating} / 5</b> stars</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div>Reputation gained: <b>+{reputationGain}</b></div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button asChild className="w-full gap-2 bg-primary text-primary-foreground hover:bg-primary/90"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <a href={bounty.githubIssueUrl} target="_blank" rel="noopener noreferrer"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Github className="size-4" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import React, { useState } from 'react'; | ||
| import { RatingStars } from './rating-stars'; | ||
|
|
||
| interface RatingModalProps { | ||
| contributor: { | ||
| id: string; | ||
| name: string; | ||
| reputation: number; | ||
| }; | ||
| bounty: { | ||
| id: string; | ||
| title: string; | ||
| }; | ||
| onSubmit: (rating: number, feedback: string) => Promise<void>; | ||
| onClose: () => void; | ||
| } | ||
|
|
||
| export const RatingModal: React.FC<RatingModalProps> = ({ contributor, bounty, onSubmit, onClose }) => { | ||
| const [rating, setRating] = useState(0); | ||
| const [feedback, setFeedback] = useState(''); | ||
| const [loading, setLoading] = useState(false); | ||
| const [error, setError] = useState<string | null>(null); | ||
| const [success, setSuccess] = useState(false); | ||
|
|
||
| const handleSubmit = async () => { | ||
| if (rating < 1 || rating > 5) { | ||
| setError('Please select a rating between 1 and 5.'); | ||
| return; | ||
| } | ||
| setLoading(true); | ||
| setError(null); | ||
| try { | ||
| await onSubmit(rating, feedback); | ||
| setSuccess(true); | ||
| } catch (e) { | ||
| setError('Failed to submit rating. Please try again.'); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| if (success) { | ||
| return ( | ||
| <div className="modal"> | ||
| <h2>Success!</h2> | ||
| <p>Rating submitted. Contributor's reputation updated.</p> | ||
| <button onClick={onClose}>Close</button> | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <div className="modal"> | ||
| <h2>Rate Contributor</h2> | ||
| <div> | ||
| <strong>Bounty:</strong> {bounty.title} | ||
| </div> | ||
| <div> | ||
| <strong>Contributor:</strong> {contributor.name} | ||
| </div> | ||
| <div> | ||
| <strong>Current Reputation:</strong> {contributor.reputation} | ||
| </div> | ||
| <div style={{ margin: '16px 0' }}> | ||
| <RatingStars value={rating} onChange={setRating} /> | ||
| </div> | ||
| <textarea | ||
| placeholder="Optional feedback" | ||
| value={feedback} | ||
| onChange={e => setFeedback(e.target.value)} | ||
| rows={3} | ||
| style={{ width: '100%', marginBottom: 8 }} | ||
| /> | ||
| {error && <div style={{ color: 'red', marginBottom: 8 }}>{error}</div>} | ||
| <button onClick={handleSubmit} disabled={loading}> | ||
| {loading ? 'Submitting...' : 'Submit'} | ||
| </button> | ||
| <button onClick={onClose} style={{ marginLeft: 8 }}>Cancel</button> | ||
| </div> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import React, { useState } from 'react'; | ||
|
|
||
| interface RatingStarsProps { | ||
| value: number; | ||
| onChange?: (value: number) => void; | ||
| disabled?: boolean; | ||
| displayOnly?: boolean; | ||
| } | ||
|
|
||
| export const RatingStars: React.FC<RatingStarsProps> = ({ value, onChange, disabled, displayOnly }) => { | ||
| const [hovered, setHovered] = useState<number | null>(null); | ||
|
|
||
| const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => { | ||
| if (!onChange || disabled || displayOnly) return; | ||
| if (e.key === 'ArrowLeft' && value > 1) onChange(value - 1); | ||
| if (e.key === 'ArrowRight' && value < 5) onChange(value + 1); | ||
| }; | ||
|
|
||
| return ( | ||
| <div | ||
| tabIndex={displayOnly ? -1 : 0} | ||
| role={displayOnly ? 'img' : 'slider'} | ||
| aria-valuenow={value} | ||
| aria-valuemin={1} | ||
| aria-valuemax={5} | ||
| onKeyDown={handleKeyDown} | ||
| style={{ display: 'flex', gap: 4, outline: 'none', cursor: displayOnly ? 'default' : 'pointer' }} | ||
| > | ||
| {[1, 2, 3, 4, 5].map((star) => ( | ||
| <span | ||
| key={star} | ||
| onMouseEnter={() => !displayOnly && setHovered(star)} | ||
| onMouseLeave={() => !displayOnly && setHovered(null)} | ||
| onClick={() => onChange && !disabled && !displayOnly && onChange(star)} | ||
| style={{ | ||
| color: (hovered ?? value) >= star ? '#FFD700' : '#CCC', | ||
| fontSize: 28, | ||
| transition: 'color 0.2s', | ||
| pointerEvents: displayOnly ? 'none' : 'auto', | ||
| userSelect: 'none', | ||
| }} | ||
|
Comment on lines
+10
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -type f -name "rating-stars.tsx" | head -20Repository: boundlessfi/bounties Length of output: 100 🏁 Script executed: cat -n ./components/rating/rating-stars.tsxRepository: boundlessfi/bounties Length of output: 1975 Ensure When Suggested fix export const RatingStars: React.FC<RatingStarsProps> = ({ value, onChange, disabled, displayOnly }) => {
const [hovered, setHovered] = useState<number | null>(null);
+ const isDisplayOnly = displayOnly || !onChange;
+ const isInteractive = !isDisplayOnly && !disabled;
const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
- if (!onChange || disabled || displayOnly) return;
+ if (!isInteractive) return;
if (e.key === 'ArrowLeft' && value > 1) onChange(value - 1);
if (e.key === 'ArrowRight' && value < 5) onChange(value + 1);
};
return (
<div
- tabIndex={displayOnly ? -1 : 0}
- role={displayOnly ? 'img' : 'slider'}
- aria-valuenow={value}
- aria-valuemin={1}
- aria-valuemax={5}
+ tabIndex={isInteractive ? 0 : -1}
+ role={isDisplayOnly ? 'img' : 'slider'}
+ aria-label={isDisplayOnly ? `${value} out of 5 stars` : 'Rating'}
+ aria-valuenow={isDisplayOnly ? undefined : value}
+ aria-valuemin={isDisplayOnly ? undefined : 0}
+ aria-valuemax={isDisplayOnly ? undefined : 5}
+ aria-disabled={isDisplayOnly ? undefined : disabled}
onKeyDown={handleKeyDown}
- style={{ display: 'flex', gap: 4, outline: 'none', cursor: displayOnly ? 'default' : 'pointer' }}
+ style={{ display: 'flex', gap: 4, outline: 'none', cursor: isInteractive ? 'pointer' : 'default' }}
>
{[1, 2, 3, 4, 5].map((star) => (
<span
key={star}
- onMouseEnter={() => !displayOnly && setHovered(star)}
- onMouseLeave={() => !displayOnly && setHovered(null)}
- onClick={() => onChange && !disabled && !displayOnly && onChange(star)}
+ onMouseEnter={() => isInteractive && setHovered(star)}
+ onMouseLeave={() => isInteractive && setHovered(null)}
+ onClick={() => isInteractive && onChange?.(star)}
style={{
color: (hovered ?? value) >= star ? '#FFD700' : '#CCC',
fontSize: 28,
transition: 'color 0.2s',
- pointerEvents: displayOnly ? 'none' : 'auto',
+ pointerEvents: isInteractive ? 'auto' : 'none',
userSelect: 'none',
}}
aria-label={star + ' star'}🤖 Prompt for AI Agents |
||
| aria-label={star + ' star'} | ||
| > | ||
| ★ | ||
| </span> | ||
| ))} | ||
| </div> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // Ensure localStorage methods are configurable and writable so tests can spyOn/set mocks | ||
| const createLocalStorageMock = () => { | ||
| let store = {} | ||
| return { | ||
| getItem: (key) => (Object.prototype.hasOwnProperty.call(store, key) ? store[key] : null), | ||
| setItem: (key, value) => { store[String(key)] = String(value) }, | ||
| removeItem: (key) => { delete store[String(key)] }, | ||
| clear: () => { store = {} }, | ||
| } | ||
| } | ||
|
|
||
| Object.defineProperty(window, 'localStorage', { | ||
| configurable: true, | ||
| writable: true, | ||
| value: createLocalStorageMock(), | ||
| }) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,12 @@ | ||||||||||||||||||||||||||||||||||||||||
| declare module "@tabler/icons-react" { | ||||||||||||||||||||||||||||||||||||||||
| import * as React from 'react' | ||||||||||||||||||||||||||||||||||||||||
| export const IconMenu2: React.FC<any> | ||||||||||||||||||||||||||||||||||||||||
| export const IconX: React.FC<any> | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| declare module "motion/react" { | ||||||||||||||||||||||||||||||||||||||||
| export const motion: any | ||||||||||||||||||||||||||||||||||||||||
| export const AnimatePresence: any | ||||||||||||||||||||||||||||||||||||||||
| export const useScroll: any | ||||||||||||||||||||||||||||||||||||||||
| export const useMotionValueEvent: any | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check if the file exists and show its content
echo "=== Checking types/shims.d.ts ==="
if [ -f "types/shims.d.ts" ]; then
echo "File exists, content:"
cat -n types/shims.d.ts
else
echo "File not found at types/shims.d.ts"
# Search for it
find . -name "shims.d.ts" -type f 2>/dev/null
fi
echo ""
echo "=== Looking for package.json in root ==="
if [ -f "package.json" ]; then
echo "Found package.json, checking dependencies:"
jq '.dependencies // {} | keys' package.json 2>/dev/null || cat package.json | head -30
fi
echo ""
echo "=== Checking for tsconfig.json ==="
if [ -f "tsconfig.json" ]; then
echo "Found tsconfig.json:"
cat tsconfig.json
fiRepository: boundlessfi/bounties Length of output: 2679 🏁 Script executed: #!/bin/bash
# Search for references to `@tabler/icons-react` and motion/react in the codebase
echo "=== References to `@tabler/icons-react` ==="
rg "@tabler/icons-react" --type ts --type tsx --type js --type jsx -B 2 -A 2
echo ""
echo "=== References to motion/react ==="
rg "motion/react" --type ts --type tsx --type js --type jsx -B 2 -A 2
echo ""
echo "=== References to framer-motion ==="
rg "framer-motion" --type ts --type tsx --type js --type jsx -B 2 -A 2Repository: boundlessfi/bounties Length of output: 279 🏁 Script executed: #!/bin/bash
# Try to find build or lint configuration files that might show errors
echo "=== Looking for build configuration ==="
find . -maxdepth 2 -type f \( -name ".eslintrc*" -o -name "eslint.config.*" -o -name "next.config.*" -o -name "tsconfig.*.json" -o -name ".github" \) 2>/dev/null | head -20
echo ""
echo "=== Checking for CI configuration ==="
if [ -d ".github/workflows" ]; then
echo "GitHub workflows found:"
ls -la .github/workflows/
echo ""
echo "Build/lint workflow content:"
cat .github/workflows/*.yml 2>/dev/null | grep -A 20 "typescript\|build\|lint\|tsc" || echo "No explicit build commands found"
fi
echo ""
echo "=== Checking package.json for devDependencies with `@types` or TypeScript ==="
cat package.json | jq '.devDependencies // empty | to_entries[] | select(.key | contains("typescript") or contains("@types") or contains("eslint"))' 2>/dev/null | head -30Repository: boundlessfi/bounties Length of output: 1647 🌐 Web query:
💡 Result: Yes —
Citations:
🌐 Web query:
💡 Result: In Motion/Framer Motion v12, the TypeScript types are built-in (the packages ship their own
Common v12 React types you can import/use(Names as exposed in the library’s docs/types)
The Typical TypeScript patternsimport { motion, type MotionProps } from "motion/react";
type MyMotionDivProps = MotionProps & React.ComponentPropsWithoutRef<"div">;
export function MyMotionDiv(props: MyMotionDivProps) {
return <motion.div {...props} />;
}import { motion } from "motion/react";
type CardProps = { title: string };
const Card = (props: CardProps) => <div>{props.title}</div>;
export const MotionCard = motion.create(Card); // typed as MotionProps & CardPropsCitations:
Remove unnecessary The Either remove the shim file entirely and rely on the packages' built-in types, or if you must keep ambient declarations, re-export the actual package exports: 🛠️ Recommended fixesOption 1: Remove the shim entirely (preferred if packages' types resolve correctly)
Option 2: Re-export actual types from the packages declare module "@tabler/icons-react" {
- import * as React from 'react'
- export const IconMenu2: React.FC<any>
- export const IconX: React.FC<any>
+ export * from "@tabler/icons-react";
}
declare module "motion/react" {
- export const motion: any
- export const AnimatePresence: any
- export const useScroll: any
- export const useMotionValueEvent: any
+ export * from "motion/react";
}📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: build-and-lint (24.x)[failure] 11-11: [failure] 10-10: [failure] 9-9: [failure] 8-8: [failure] 4-4: [failure] 3-3: 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.